Jump to content
Sign in to follow this  
EarthQuake

Adventure: Playfield Manipulation Midscreen...?

Recommended Posts

So I was working on my Adventure hack a bit, overwhelmed with the new possibilities given Nukey's new kernal. Basically, each line of the playfield data has it's own value (stored in the lower nybble of the byte sent to PF0), which allows not only higher vertical resolution in bitmaps, but screen bitmaps where each playfield row can be of variable height, so you can increase the vertical resolution only where you need it.

 

But, anyways, I was wondering about something and it might just be possible. IIRC, the bit in the playfield control register that controls the mirroring, could theoretically be switched on and off as the screen bitmap is drawn to achieve a playfield layout that is somewhat non-symmetrical. The engine could look for a certain bit set in the screen bitmap's PF0 byte and XOR the bit sent to the playfield register, effectively toggling it each time the condition is met. I know I'm always going on about how Adventure could extremely benefit from asymmetrical playfields, but this idea could be a great compromise.

 

Would this even be possible? It's a long shot, but I can see amazing things resulting from this.

 

Here's a textual result of a room that would take advantage of such an addition:

 

 .byte $F0,$FF,$0F;XXXXXXXXXXXXXXXX		RRRRRRRRRRRRRRRR
.byte $30,$00,$00;XX									RR
.byte $30,$00,$00;XX									RR
.byte $F0,$C0,$FC;XXXXXX		XXXXXXRRRRRR		RRRRRR
.byte $30,$00,$00;XX				  MM				   <- *
.byte $30,$00,$00;XX				  MM				  
.byte $F0,$FF,$FF;XXXXXXXXXXXXXXXXXXXXMMMMMMMMMMMMMMMMMMMM
* Playfield register updated on noted line.

 

The value specified in the room data table could perhaps just be used as the initial value of the mirroring behavior.

Edited by EarthQuake

Share this post


Link to post
Share on other sites

Not enough time...at least not without caching something in Ram or rewriting the kernal to use some kind of skipdraw variant. The program is already printing 2 sprites, then indirectly-loading and storing PF0 and bumping the index. So you are faced with the problem of either a loaded CTRLPF or PF1 being written too late (leading to playfield gfx clipping). It's not an issue for storing an extra value to a line counter, because this is perfectly fine to store after the playfield has already been updated. The indirect loads are cycle hogs.

 

A possible workaround that does not require rewrites would be to not use PF0 at all. This cuts the horizontal display down by 8 pixels, but allows you to use that read to update CTRLPF instead of PF0. You could still implement a line counter (because just as before, writes for this are not time-critical). Maximum X and Y values throughout the program would need editing.

 

Another possible solution would be to split PF0, PF1 and PF2 data into seperate tables (with seperate RoomLo/RoomHi pointers to each table). You save 4 cycles here, because INY only needs to happen once per block of data (instead of 3 times). Those cycles can be used after storing PF0...

 

AND #$01 ;keep only the low bit

ORA #$20 ;merge in the ball size

STA CTRLPF ;store the value

 

This still happens just a tad too late (due to the extra 3 cycles for a store), but it looks like only the upper line is affected. Another drawback is that the line counter is limited to even values only (use AND #$0E instead of AND #$0F to trim off the CTRLPF bit in the PF0/linecount table), but that's not as serious.

Share this post


Link to post
Share on other sites

Quick 'n' dirty...

 

Alternate method allows CTRL stores instead of a line counter (the screens use the same 21-byte format as the original game). The initial branch has been moved to the top of the routine to avoid the use of a branch later. As mentioned, there's a 2-cycle overrun that causes CTRL to hit just a little late for the very first scanline.

 

PrintDisplay:;draw a frame...
   STA	HMCLR			  ;3 Clear horizontal motion
   LDA	Obj1X			  ;3 Position Player00 Sprite to the X Coordinate of Object1
   LDX	#$00			   ;2
   JSR	PosSpriteX		 ;6
   LDA	Obj2X			  ;3 Position Player01 Sprite to the X Coordinate of Object2
   INX					   ;2 (the subroutine PosSpriteX didn't change X)
   JSR	PosSpriteX		 ;6
   LDA	PlayerX			;3 Position Ball Strite to the X Coordinate of the Man
   LDX	#$04			   ;2
   JSR	PosSpriteX		 ;6
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STA	HMOVE			  ;3 Apply Horizontal Motion
   STA	CXCLR			  ;3 Clear Collision Latches
   LDY	#$07			   ;2 Use 7 lines for top line
   STY	PixelCount		 ;3 Store to line counter
PrintDisplay_1:
   LDX	INTIM			  ;4 Wait for end of the current frame
   BNE	PrintDisplay_1	 ;2
   STX	Player0Def		 ;3 Set Player00 definition index
   STX	Player1Def		 ;3 Set Player01 definition index
   STX	RoomDefIndex	   ;3 Set room definition index
   STX	GRP1			   ;3 Clear any graphics for Player01
   INX					   ;2
   STX	VDELP1			 ;3 vertically delay Player 01
;----------------------------------
;skip these lines if you are using unshared variables for RoomLo/Hi
   LDA	Control			;3 Get 3-frame room number
   AND	#$7F			   ;2 (strip inactive game bit)
   TAY					   ;2
   LDA	RoomDataTable0,Y   ;4 Get the room gfx LSB pointer
   STA	RoomLo			 ;3
   LDA	RoomDataTable1,Y   ;4 Get the room gfx MSB pointer
   STA	RoomHi			 ;3
;----------------------------------
   LDA	PlayerY			;3 Get the Y Coordinate of the Man
   SEC					   ;2
   SBC	#$04			   ;2 And Adjust it (By Four Scan Lines) for printing
   STA	PlayerYadj		 ;3 (so Y Coordinate Specifies Middle)
   CMP	#$64			   ;2 check if ball is on the upper portion of the screen
   PHP					   ;3 push carry status
   PLA					   ;4 pull as accumulator
   ASL					   ;2 ...and shift carry to bit 1
   LDY	#$00			   ;2 Set initial room definition index
   LDX	#$68			   ;2
   STX	ScanLineCnt		;3 Set initial Scan Line Count
;NOTE: bankswitched kernal would begin here------------------------------------------
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STY	VBLANK			 ;3 Clear any Vertical Blank
   BPL	Print_Top_Line	 ;2 always branch

Print_More_Lines:
   LDY	#$0F			   ;2 Enable Ball Graphic
   STY	PixelCount		 ;3 Store to line counter
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STX	GRP0			   ;3 Display Player00 definition byte (if Wanted)
Print_Top_Line:
   STA	ENABL			  ;3 Enable Ball (If Wanted)
   LDY	RoomDefIndex	   ;3 Get room definition index
   LAX	(RoomLo),Y		 ;5 Get first room definition byte (use LAX to preserve)
   STA	PF0				;3 ...and display
   INY					   ;2 bump index
   LDA	(RoomLo),Y		 ;5 Get next room definition byte
   STA	PF1				;3 ...and display
   INY					   ;2 bump index
   LDA	(RoomLo),Y		 ;5 Get next room definition byte
   STA	PF2				;3 ...and display
   TXA					   ;2 Get PF0 definition back
   AND	#$0F			   ;2 keep only low nybble for CTRL
   ORA	#$20			   ;2 merge in ball size
   STA	CTRLPF			 ;3 Store to line counter
   INY					   ;2 bump index
   STY	RoomDefIndex	   ;3 ...and save for Next Time
   LAX	ScanLineCnt		;3 Get the scan line. Use LAX for subrtaction later
   DEX					   ;2 (save 3 cycles by using DEX instead of DEC)
   CPX	#$08			   ;2 Have we reached to within 8 scanlines of the bottom?
PrintPlayer01:;Print Player01 (Object 02)
   STX	ScanLineCnt		;3 Store the scan line
   LDY	Player1Def		 ;3 Get the Player01 definition index
   CLC					   ;2 Use carry to kill off difference between X and A
   SBC	Obj2Y			  ;3 Have we reached Object2's Y Coordinate?
   STA	WSYNC			  ;3 Wait for horizontal Blank
   BPL	PrintPlayer00	  ;2 ...If Not, Branch
   LDA	(Obj2Lo),Y		 ;5 Get the Next Player01 Definition byte
   STA	GRP1			   ;3 ...and display
   BEQ	PrintPlayer00	  ;2 If Zero then Definition finished
   INC	Player1Def		 ;5 Goto next Player01 definition byte
PrintPlayer00:;Print Player00 (Object01), Ball (Man) and Room.
   TXA					   ;3 Get Current Scan Line
PrintPlayer00_0:
   LDX	#$00			   ;2 Clear X (player 0 gfx)
   SEC					   ;2
   SBC	Obj1Y			  ;2 Have we reached the Object1's Y coordinate?
   BPL	PrintPlayer00_1	;2 If not then Branch
   LDY	Player0Def		 ;3 Get Player00 definition index
   LAX	(Obj1Lo),Y		 ;5 Get the Next Player00 definition byte (give to X)
   BEQ	PrintPlayer00_1	;2 If Zero then Definition finished
   INC	Player0Def		 ;5 Go to Next Player00 definition byte
PrintPlayer00_1:
   LDA	ScanLineCnt		;3 Get Scan line count
   SEC					   ;2
   SBC	PlayerYadj		 ;2 Have we reached the Man's Y Coordinate?
   AND	#$FC			   ;2 Mask value to four either side (getting depth of 8)
   BNE	PrintPlayer00_2	;2 If >4 on either end, branch (skip ball display)
   LDA	#$02			   ;2 Enable Ball Graphic
PrintPlayer00_2:
   DEC	PixelCount		 ;5 decrement the line counter
   BMI	Print_More_Lines   ;2 if more lines remain, branch


PrintPlayer00_3:
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STA	ENABL			  ;3 Enable Ball (If Wanted)
   STX	GRP0			   ;3 Display Player00 definition byte (if Wanted)
PrintPlayer00_4:
   LAX	ScanLineCnt		;3 Get the scan line. Use LAX for subrtaction later
   DEX					   ;2 (save 3 cycles by using DEX instead of DEC)
   CPX	#$08			   ;2 Have we reached to within 8 scanlines of the bottom?
   BPL	PrintPlayer01	  ;2 If not, Branch
   STX	VBLANK			 ;3 Turn on VBLANK, now 3 cycles earlier to fix side pixel
;NOTE: bankswitched kernal would end here--------------------------------------------
   LDA	#$00			   ;2
   STA	GRP1			   ;3 Clear any graphics for Player01
   STA	GRP0			   ;3 Clear any graphics for Player00
   LDA	#$20			   ;2
   STA	TIM64T			 ;4 Start timing this frame
   RTS					   ;6 return to main loop

 

Then edit all of the PF0 data to include CTRLPF values for the lower nybble.

Share this post


Link to post
Share on other sites

Update...I've got it figured out.

 

Instead of bumping a Y index for each of the 2 sprites, bump it's indirect vector LSBs instead. Y can therefore remain set at zero when setting up the sprite display (saves 4 more cycles). There was also a superfluous instruction which has been removed.

 

Because the indirect vectors are bumped, I stored the original values to the now-unused Player#def variables...and pulled them back after the display is drawn (so that they remain unchanged for the other 2 display frames).

 

As before, the scanline count is placed in the low nybble of PF0. However, EACH line must contain a new CTRLPF value...4 values per line (PF0, PF1, PF2, CTRL). This last value should contain ball size ($20) + priority ($00 = dark, $04 = normal) + display mode ($00 = mirrored/$01 = reversed). Not enough time to implement other bits, so you'll still need to set panels up in the setup routine (the store to CTRLPF can be removed, tho).

 

Here's the updated kernal...

 

PrintDisplay:;draw a frame...
   STA	HMCLR			  ;3 Clear horizontal motion
   LDA	Obj1X			  ;3 Position Player00 Sprite to the X Coordinate of Object1
   LDX	#$00			   ;2
   JSR	PosSpriteX		 ;6
   LDA	Obj2X			  ;3 Position Player01 Sprite to the X Coordinate of Object2
   INX					   ;2 (the subroutine PosSpriteX didn't change X)
   JSR	PosSpriteX		 ;6
   LDA	PlayerX			;3 Position Ball Strite to the X Coordinate of the Man
   LDX	#$04			   ;2
   JSR	PosSpriteX		 ;6
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STA	HMOVE			  ;3 Apply Horizontal Motion
   STA	CXCLR			  ;3 Clear Collision Latches
PrintDisplay_1:
   LDX	INTIM			  ;4 Wait for end of the current frame
   BNE	PrintDisplay_1	 ;2
   STX	RoomDefIndex	   ;3 Set room definition index
   STX	GRP1			   ;3 Clear any graphics for Player01
   INX					   ;2
   STX	VDELP1			 ;3 vertically delay Player 01
;----------------------------------------------------
;do not use these lines if RoomLo/Hi is not shared...
   LDA	Control			;3 Get 3-frame room number
   AND	#$7F			   ;2 (strip inactive game bit)
   TAY					   ;2
   LDA	RoomDataTable0,Y   ;4 Get the room gfx LSB pointer
   STA	RoomLo			 ;3
   LDA	RoomDataTable1,Y   ;4 Get the room gfx MSB pointer
   STA	RoomHi			 ;3
;----------------------------------------------------
   LDA	Obj1Lo			 ;3 transfer low sprite1 pointer to temp
   STA	Player0Def		 ;3
   LDA	Obj2Lo			 ;3 transfer low sprite1 pointer to temp
   STA	Player1Def		 ;3
   LDA	PlayerY			;3 Get the Y Coordinate of the Man
   SEC					   ;2
   SBC	#$04			   ;2 And Adjust it (By Four Scan Lines) for printing
   STA	PlayerYadj		 ;3 (so Y Coordinate Specifies Middle)
   LDY	#$00			   ;2 Set initial room definition index
   LDX	#$68			   ;2
   STX	ScanLineCnt		;3 Set initial Scan Line Count
;NOTE: bankswitched kernal would begin here------------------------------------------
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STY	VBLANK			 ;3 Clear any Vertical Blank
   BPL	Print_Top_Line	 ;2 always branch (X=$68)

Print_More_Lines:
   LDY	RoomDefIndex	   ;3 Get room definition index
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STX	GRP0			   ;3 Display Player00 definition byte (if Wanted)
   STA	ENABL			  ;3 Enable Ball (If Wanted)
Print_Top_Line:
   LAX	(RoomLo),Y		 ;5 Get first room definition byte (use LAX to preserve)
   STA	PF0				;3 ...and display
   INY					   ;2 bump index
   LDA	(RoomLo),Y		 ;5 Get next room definition byte
   STA	PF1				;3 ...and display
   INY					   ;2 bump index
   LDA	(RoomLo),Y		 ;5 Get next room definition byte
   STA	PF2				;3 ...and display
   INY					   ;2 bump index
   LDA	(RoomLo),Y		 ;5 Get row's CTRL value
   STA	CTRLPF			 ;3 Store to playfield control
   TXA					   ;2 Get PF0 definition back
   AND	#$0F			   ;2 keep only low nybble of PF0
   STA	PixelCount		 ;3 Store to line counter
   INY					   ;2 bump index
   STY	RoomDefIndex	   ;3 ...and save for Next Time
   LAX	ScanLineCnt		;3 Get the scan line. Use LAX for subrtaction later
   DEX					   ;2 (save 3 cycles by using DEX instead of DEC)
PrintPlayer01:;Print Player01 (Object 02)
   STX	ScanLineCnt		;3 Store the scan line
   LDY	#$00			   ;2 clear Y index for both indirect loads
   CLC					   ;2 Use carry to kill off difference between X and A
   SBC	Obj2Y			  ;3 Have we reached Object2's Y Coordinate?
   STA	WSYNC			  ;3 Wait for horizontal Blank
   BPL	PrintPlayer00	  ;2 ...If Not, Branch
   LDA	(Obj2Lo),Y		 ;5 Get the Next Player01 Definition byte
   STA	GRP1			   ;3 ...and display
   BEQ	PrintPlayer00	  ;2 If Zero then Definition finished
   INC	Obj2Lo			 ;5 Goto next Player01 definition byte
PrintPlayer00:;Print Player00 (Object01), Ball (Man) and Room.
   TXA					   ;3 Get Current Scan Line
   LDX	#$00			   ;2 Clear X (player 0 gfx)
   SEC					   ;2
   SBC	Obj1Y			  ;2 Have we reached the Object1's Y coordinate?
   BPL	PrintPlayer00_1	;2 If not then Branch
   LAX	(Obj1Lo),Y		 ;5 Get the Next Player00 definition byte (give to X)
   BEQ	PrintPlayer00_1	;2 If Zero then Definition finished
   INC	Obj1Lo			 ;5 Goto next Player00 definition byte
PrintPlayer00_1:
   LDA	ScanLineCnt		;3 Get Scan line count
   SEC					   ;2
   SBC	PlayerYadj		 ;2 Have we reached the Man's Y Coordinate?
   AND	#$FC			   ;2 Mask value to four either side (getting depth of 8)
   BNE	PrintPlayer00_2	;2 If >4 on either end, branch (skip ball display)
   LDA	#$02			   ;2 Enable Ball Graphic
PrintPlayer00_2:
   DEC	PixelCount		 ;5 decrement the line counter
   BMI	Print_More_Lines   ;2 if more lines remain, branch
PrintPlayer00_3:
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STA	ENABL			  ;3 Enable Ball (If Wanted)
   STX	GRP0			   ;3 Display Player00 definition byte (if Wanted)
PrintPlayer00_4:
   LAX	ScanLineCnt		;3 Get the scan line. Use LAX for subrtaction later
   DEX					   ;2 (save 3 cycles by using DEX instead of DEC)
   CPX	#$08			   ;2 Have we reached to within 8 scanlines of the bottom?
   BPL	PrintPlayer01	  ;2 If not, Branch
   STX	VBLANK			 ;3 Turn on VBLANK, now 3 cycles earlier to fix side pixel
;NOTE: bankswitched kernal would end here--------------------------------------------
   LDA	#$00			   ;2
   STA	GRP1			   ;3 Clear any graphics for Player01
   STA	GRP0			   ;3 Clear any graphics for Player00
   LDA	Player0Def		 ;3 Restore low pointer vectors...
   STA	Obj1Lo			 ;3
   LDA	Player1Def		 ;3
   STA	Obj2Lo			 ;3
   LDA	#$20			   ;2
   STA	TIM64T			 ;4 Start timing this frame
   RTS					   ;6 return to main loop

 

 

Thanks for the suggestion! :) This is a very good addition. Each screen will require at least 28 bytes...but it's still possible to share bytes if you are careful.

 

NOTE: Because the LSB's of the sprite vector is updated, it's -required- that sprites DO NOT cross a page boundry under any circumstances.

Share this post


Link to post
Share on other sites

BTW since each "band" of screen gfx holds it's own ball size and priority, you could make the player sprite wider or thinner, or allow game objects to move behind playfield obstructions (hiding them from view!)...depending on what row is involved. A new wrinkle to hiding things ;)

Share this post


Link to post
Share on other sites

Wow! I wasn't expecting another kernal from you! :lol:

 

I never thought about adding another byte to each row in the bitmap to hold the playfield control. This has very huge implications. I can't find an immediate use for all the new things that are possible, but hopefully someone in the future may take advantage of it in it's entirety. The real groundbreaking thing here is being able to modify the playfield mirroring on-the-fly, allowing some really neat new worlds to be created.

 

I'm definitely going to give this a try. The added byte per room is a killer though, especially when you got rooms that are 36, 45, 51, 63 bytes et cetera... but I think the new possibilities far outweigh the disadvantages. :)

 

Some quick questions: because each row of playfield data contains the playfield control, does this make the byte in the room data table redundant or anything? Afaik, the engine still uses it for determining if a room is dark and if the missiles should display. Also, in my source, it checks the byte if the room is set as dark and establishes and recolors the surround object. The only thing that is redundant is the mirroring bit, correct? Will this interfere at all?

Edited by EarthQuake

Share this post


Link to post
Share on other sites
Wow! I wasn't expecting another kernal from you! :lol:
It's a very cool idea that just begs to be exploited, so I couldn't set it down until I had it nailed.

 

 

I never thought about adding another byte to each row in the bitmap to hold the playfield control. This has very huge implications. I can't find an immediate use for all the new things that are possible, but hopefully someone in the future may take advantage of it in it's entirety. The real groundbreaking thing here is being able to modify the playfield mirroring on-the-fly, allowing some really neat new worlds to be created.
Completely agree...and it just looks better than throwing panels all over the place :)

 

 

I'm definitely going to give this a try. The added byte per room is a killer though, especially when you got rooms that are 36, 45, 51, 63 bytes et cetera... but I think the new possibilities far outweigh the disadvantages. :)
This is where 8k versions become mandatory. That extra byte for each band burns 217 bytes alone for Adventure's original 31 screens (eating up most of the free space in 4k). No big deal in the 8k version's 3k of empty space. Too bad that it can't be cut down to a single bit...the AND'ing and OR'ing takes too much time.

 

 

Some quick questions: because each row of playfield data contains the playfield control, does this make the byte in the room data table redundant or anything? Afaik, the engine still uses it for determining if a room is dark and if the missiles should display. Also, in my source, it checks the byte if the room is set as dark and establishes and recolors the surround object. The only thing that is redundant is the mirroring bit, correct? Will this interfere at all?
The AND and STA CTRLPF above ObjectSort is now not needed (because each band sets it's own value)...setting it in the setup routine makes no difference. But yeah, the panels and surround still require that byte to function...unfortunately. That's 3 bits, so it's pretty difficult to merge it with something else. They can be grouped to the lower 3 bits now, tho...saving a bit of time.

 

 

The best news is that it works with the previous additions with no gfx clipping and no cycle time lost :) So it's there if anybody needs it...I made it part of the 8k template that I was working on.

Edited by Nukey Shay

Share this post


Link to post
Share on other sites

Hmm, I merged the new kernal with my source code, but I have several problems.

 

First, the objects are flickering obsessively. Not only that, but upon closer examination of each frame, the playfield seems to be changing priority continuously because one frame, a dragon would appear in front of the playfield, and on another frame it would appear behind it. Also, dragons have a difficult time biting, probably due to the bad flickering. They also seem to be cycling through states at times.

 

I worked on this at my parent's house and there were quite a few distractions, so I'm going to give this another try and see if I get the same results. It might have had to do with where I made changes so that it was compatible with the indirect Y loading that my source uses.

 

And what does this mean? Is it applicable to me, or should I just remove it?

;do not use these lines if RoomLo/Hi is not shared...

 

The room display works perfectly. It's just the objects that are having the problem. I'll post back here if I figure out what it was. Oh hah, by the way, one of my bitmaps is 80 bytes, with a total of four bitmaps on the entire page filling it up exactly. Thankfully I have about 11 more pages reserved for screen bitmaps, and those four bitmaps are the most detailed of the bunch. I'm not going to have any problems filling up my entire 8k. :P

Edited by EarthQuake

Share this post


Link to post
Share on other sites

Got 'er. :P

 

It was indeed something I forgot. I didn't update the return code for the second bank.

 

Now to see what neat little things I can come up with. >:)~

Share this post


Link to post
Share on other sites
I was wondering about something and it might just be possible. IIRC, the bit in the playfield control register that controls the mirroring, could theoretically be switched on and off as the screen bitmap is drawn to achieve a playfield layout that is somewhat non-symmetrical. The engine could look for a certain bit set in the screen bitmap's PF0 byte and XOR the bit sent to the playfield register, effectively toggling it each time the condition is met. I know I'm always going on about how Adventure could extremely benefit from asymmetrical playfields, but this idea could be a great compromise.

 

Would this even be possible? It's a long shot, but I can see amazing things resulting from this.

 

Here's a textual result of a room that would take advantage of such an addition:

 

 .byte $F0,$FF,$0F;XXXXXXXXXXXXXXXX		RRRRRRRRRRRRRRRR
.byte $30,$00,$00;XX									RR
.byte $30,$00,$00;XX									RR
.byte $F0,$C0,$FC;XXXXXX		XXXXXXRRRRRR		RRRRRR
.byte $30,$00,$00;XX				  MM				   <- *
.byte $30,$00,$00;XX				  MM				  
.byte $F0,$FF,$FF;XXXXXXXXXXXXXXXXXXXXMMMMMMMMMMMMMMMMMMMM
* Playfield register updated on noted line.

 

The value specified in the room data table could perhaps just be used as the initial value of the mirroring behavior.

From Nukey's replies, it sounds like he's already accommodated you. I'm just writing to point out that at least one game used this type of technique-- Tutankham for the Atari 2600. I'm looking forward to seeing what you do with it. :)

 

Michael

Share this post


Link to post
Share on other sites

It's difficult designing playfields using this. I'm tempted to write a new playfield editor (as if I haven't already written enough of them :lol:), but I haven't a strong direction about how to do the variable row heights with the most flexibility possible without cluttering up the interface.

 

I'll probably do things by hand until I get fed up enough to work on a new editor. I'm anxious myself to see what I can come up with. :)

Share this post


Link to post
Share on other sites

And what does this mean? Is it applicable to me, or should I just remove it?

;do not use these lines if RoomLo/Hi is not shared...

In the setup routine in the original game, RoomLo and RoomHi are stored (the vector for the screen gfx). To save 2 bytes of ram, these variables can be shared with others.

 

In the setup routine here, the room number is stored to another ram location (Control, in this case). When the display executes, the program uses this other ram location to fetch the room number - which is then used as an offset to load the RoomLo/Hi vector. The reason that the routine does not use PlayerRoom is because the collision routine bounces it around a little bit (Try it yourself...replace LDA Control with LDA PlayerRoom and bounce against a wall as you flip screens).

Share this post


Link to post
Share on other sites

I found a small glitch, Nukey. It's pretty insignificant, but if you set the playfield priority above the missiles, then missile0 overlaps the playfield by one scanline. It might be something that was present in the original adventure, or it might be a result of your improved kernal. Just set the playfield priority of any screen with missile 0 enabled. In my attached example, I set the priority about halfway down the screen and onwards. Missile 1 is pixel-perfect in the right image.

 

If you can't reproduce the problem in your sources, then it's probably something I did. I was repositioning the missiles and I modified their width. Like I said, it's nothing major though, or at least I hope not.

 

:lol:

post-5734-1226655805_thumb.png

post-5734-1226655810_thumb.png

Edited by EarthQuake

Share this post


Link to post
Share on other sites
I found a small glitch, Nukey. It's pretty insignificant, but if you set the playfield priority above the missiles, then missile0 overlaps the playfield by one scanline. It might be something that was present in the original adventure, or it might be a result of your improved kernal. Just set the playfield priority of any screen with missile 0 enabled. In my attached example, I set the priority about halfway down the screen and onwards. Missile 1 is pixel-perfect in the right image.

It's because priority isn't set until halfway through the scanline. There's no good solution to this...setting priority earlier would interfere with timing of playfield display. This is not an issue with the original game, because priority is only set once there.

 

To fix the problem, even more cycles would need to be saved. One way would be to break up playfield data into 4 tables for each screen so that the multiple INY's are not needed (but increasing RAM overhead, because 4 display vectors would be required instead of only 1).

 

	   LAX	(PF0vector),Y	  ;5 Get first room definition byte
   STA	PF0				;3 ...and display
   LDA	(CTRLvector),Y	 ;5 Get row's CTRL value
   STA	CTRLPF			 ;3 Store to playfield control
   LDA	(PF1vector),Y	  ;5 Get next room definition byte
   STA	PF1				;3 ...and display
   LDA	(PF2vector),Y	  ;5 Get next room definition byte
   STA	PF2				;3 ...and display

Share this post


Link to post
Share on other sites

BTW if you are concerned about rom space wasted for generic screen layouts, you can cut this down by swapping CTRL with the linecounter (so that linecount values greater than 16 can be used). The code change is pretty simple (shown in lower-case)...

 

Print_Top_Line:
   LAX	(RoomLo),Y		 ;5 Get first room definition byte
   STA	PF0				;3 ...and display
   INY					   ;2 bump index
   LDA	(RoomLo),Y		 ;5 Get next room definition byte
   STA	PF1				;3 ...and display
   INY					   ;2 bump index
   LDA	(RoomLo),Y		 ;5 Get next room definition byte
   STA	PF2				;3 ...and display
   INY					   ;2 bump index
;added lines...
   txa					   ;2 Get PF0 definition back
   and	#$0F			   ;2 keep only low nybble of PF0
   ora	#$20			   ;2 merge in the ball size
   sta	CTRLPF			 ;3 Store to playfield control

   LDA	(RoomLo),Y		 ;5 Get number of scanlines
;removed lines...
;	   STA	CTRLPF			 ;3 Store to playfield control
;	   TXA					   ;2 Get PF0 definition back
;	   AND	#$0F			   ;2 keep only low nybble of PF0
   STA	PixelCount		 ;3 Store to line counter
   INY					   ;2 bump index
   STY	RoomDefIndex	   ;3 ...and save for Next Time
   LAX	ScanLineCnt		;3 Get current scan line
   DEX					   ;2

...and...

 

PrintPlayer00_2:
   DEC	PixelCount		 ;5 decrement the line counter
   beq	Print_More_Lines   ;2 branch if end of this band

 

 

Any screen could then be as small as 4 bytes for the entire display. Here's the game select screen...

 

Room00:;Number Room Definition
   .byte $F1,$FF,$FF,$08;8 scanlines
   .byte $31,$00,$00,$50;80 scanlines
   .byte $F1,$FF,$0F,$10;16 scanlines

Edited by Nukey Shay

Share this post


Link to post
Share on other sites

Ah! Thank you so much, I will definitely swap the CTRLPF and scanline count. It makes more sense that way anyhow. The only thing I will not be able to do on a line per line basis is change the ball width (because it needs the upper nybble), but I never planned on doing that in the first place. I had a lot of places in my room bitmaps where I would need to use multiple identical lines to fill up the screen. I can probably free up to an entire page of space or more with those changes! :)

 

The bitmap you see in the screenshots above are 52 bytes. With the new code, they are 40. A 24 byte savings between just two bitmaps. :)

 

Odyssey is going to be one sweet-looking Adventure hack!

 

Oh, you probably haven't seen anything yet. I took into account the massive ROM usage when I implemented Nukey's new (new new) kernal. So I had enough room for about 4-6 screens per page and still make my 55 room goal. Now with the extra byte savings, I can make some rooms more detailed than planned. :P

Edited by EarthQuake

Share this post


Link to post
Share on other sites

The bad thing about variable ball sizes is that it could skew when moving in the middle of the display (because CTRL isn't reset until after the left half had been drawn). If you happen to be moving on the start of a new band (which uses a different ball width), the upper line of the ball would be clipped. The kernal also doesn't include variable ball height. There are also other issues regarding using the horizontal location in calculations (such as placing the surround object). So this is a better use of the definition tables all around.

Share this post


Link to post
Share on other sites

88 bytes saved between all the screens I had done so far. :D

 

I only have about 11 screens or so completed, so that's about 1/5th of the total I had planned. Add up those savings, and I was I correct, probably going to have an entire page of ROM that I can use to make stuff even more detailed.

Share this post


Link to post
Share on other sites

Actually, you can increase the rom size to 16k or 32k if more space is needed. Just have each bank hold a specific stretch of rooms, check which one is currently displayed, and bankswitch accordingly. You'll need to keep your sprites positioned exactly the same in each bank (tho you could make them look different if desired). A possible use of this idea is to have all dark rooms in one bank, all lighted rooms in another...and have the dragons and things look slightly different in the "dark" :)

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.
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...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...