Jump to content
IGNORED

Adventure: Playfield Manipulation Midscreen...?


EarthQuake

Recommended Posts

  • 1 month later...

I haven't figured out much use for the free cycles mentioned without utilizing ram in some way (which is contrary to what this template is intended for)...but 4 of these cycles could be used to reset the GFX delimiter to any value instead of using zero. An easy way to add blank lines to any bitmap instead of using dots. This is done by setting a variable, let's call it GFX_DELIMITER, to a value unused by all gfx in the assembly...and adding a CMP instruction following the 2 loading instructions to check for this specific value.

 

Altered display routine:

KernalDone:
   BIT	$FFF9			  ;4 select 2nd bank
   LDY	#$00			   ;2 Set initial room definition index
   LAX	(RoomLo),Y		 ;5 Get first room definition byte (use LAX to preserve)
   STA	WSYNC			  ;3 Wait for horizontal Blank
   STY	VBLANK			 ;3 Clear any Vertical Blank
   JMP	Print_Top_Line	 ;3

No_Playfield_Update:;(63)
;NOTE: 10 unused cycles here...the Y register is free (to update color or whatever)
   STA	WSYNC			  ;3 00 Wait for horizontal Blank
   STA	ENABL			  ;3 03 Enable Ball (If Wanted)
   STX	GRP0			   ;3 06 Display Player00 definition byte (if Wanted)
   LAX	ScanLineCnt		;3 09 Get the scan line. Use LAX for subtraction later
   DEX					   ;2 11 (save 3 cycles by using DEX instead of DEC)
   CPX	#$08			   ;2 13 Have we reached to within 8 scanlines of the bottom?
   BPL	Waste_A_Lot		;2 15 If not, Branch
   STX	VBLANK			 ;3 18 Turn on VBLANK, now 3 cycles earlier to fix side pixel
   BMI	KernalDone		 ;2 always branch...display finished

Enable_Ball:;53
   LDA	#$02			   ;2 55 Enable Ball Graphic
Disable_Ball:;55
   DEC	PixelCount		 ;5 60 decrement the line counter
   BNE	No_Playfield_Update;2 62/63 if more lines remain, branch
;NOTE: 3 unused cycles here...the Y register is free (to update color or whatever)
   STA	$2E				;3 65
   LDY	RoomDefIndex	   ;3 68 Get room definition index
   INY					   ;2 70 bump index
   STA	ENABL			  ;3 73 Enable Ball (If Wanted)
   STX	GRP0			   ;3 76 Display Player00 definition byte (if Wanted)
   LAX	(RoomLo),Y		 ;5 05 Get first room definition byte (use LAX to preserve)
Print_Top_Line:
   STA	ENAM0			  ;3 08 Enable left hand wall(if wanted-missle00)
   STA	PF0				;3 11 ...and display
   INY					   ;2 13 bump index
   LDA	(RoomLo),Y		 ;5 16 Get next room definition byte
   STA	PF1				;3 19 ...and display
   INY					   ;2 21 bump index
   LDA	(RoomLo),Y		 ;5 26 Get next room definition byte
   STA	PF2				;3 29 ...and display
   INY					   ;2 31 bump index
   TXA					   ;2 33 Get PF0 definition back
   AND	#$0D			   ;2 35 keep only low nybble of PF0
   ORA	#$20			   ;2 37 merge in the ball size
   STA	CTRLPF			 ;3 40 Store to playfield control
   LSR					   ;2 42
   LSR					   ;2 44
   STA	ENAM1			  ;3 47 Enable right hand wall(if wanted-missle01)
   LDA	(RoomLo),Y		 ;5 52 Get number of scanlines for this row
   STA	PixelCount		 ;3 55 Store to line counter
   STY	RoomDefIndex	   ;3 58 ...and save for Next Time
   LAX	ScanLineCnt		;3 61 Get the scan line. Use LAX for subrtaction later
   DEX					   ;2 63 (save 3 cycles by using DEX instead of DEC)
   LDY	#$00			   ;2 65 clear Y index
   STX	ScanLineCnt		;3 68 Store the scan line
PrintPlayer01:;Print Player01 (Object 02)
   SBC	Obj2Y			  ;3 71 Have we reached Object2's Y Coordinate?
   BPL	Waste11			;2 73 ...If Not, Branch
   LDA	(Obj2Lo),Y		 ;5 02 Get the Next Player01 Definition byte
   CMP	#GFX_DELIMITER	 ;2 04
   BEQ	Waste4			 ;2 06 If Zero then Definition finished
   STA	GRP1			   ;3 09 ...and display
   INC	Obj2Lo			 ;5 14 Goto next Player01 definition byte
PrintPlayer00:;Print Player00 (Object01), Ball (Man) and Room.
   TXA					   ;2 16 Get Current Scan Line
   LDX	#$00			   ;2 18 Clear X (player 0 gfx)
   SEC					   ;2 20
   SBC	Obj1Y			  ;3 23 Have we reached the Object1's Y coordinate?
   BPL	Waste11_2		  ;2 25 If not then Branch
   LAX	(Obj1Lo),Y		 ;5 30 Get the Next Player00 definition byte (give to X)
   CMP	#GFX_DELIMITER	 ;2 32
   BEQ	Waste4_2		   ;2 34 If Zero then Definition finished
   INC	Obj1Lo			 ;5 39 Goto next Player00 definition byte
   LDA.w  ScanLineCnt		;4 43 Get Scan line count
   SEC					   ;2 45
PrintPlayer00_1:;45
   SBC	PlayerYadj		 ;3 48 Have we reached the Man's Y Coordinate?
   AND	#$FC			   ;2 50 Mask value to four either side (getting depth of 8)
   BEQ	Enable_Ball		;2 52 If >4 on either end...
   BNE	Disable_Ball	   ;2 54 branch (skip ball display)

Waste11:;74
   DEC	$2E				;5 03
   NOP					   ;2 05
   NOP					   ;2 07
Waste4:;07
   STY.w  GRP1			   ;4 11
   BPL	PrintPlayer00	  ;2 14 always branch

Waste11_2:;26
   DEC	$2E				;5 31
   NOP					   ;2 33
   NOP					   ;2 35
Waste4_2:;35
   LDX	#$00			   ;2 37
   LDA	ScanLineCnt		;3 40
   SEC					   ;2 42
   BCS	PrintPlayer00_1	;2 45 always branch

Waste_A_Lot:;16
;NOTE: this part wastes 46 cycles and resets Y to zero...all registers are OK to use,
;	  but A and X must be = ScanLineCnt and Y = 0 and carry clear on exit
   LDY	#$09			   ;2 18
WasteLoop:
   DEY					   ;2 20/25/30/35/40/45/50/55/60
   BNE	WasteLoop		  ;2 23/28/33/38/43/48/53/58/62
   STX.w  ScanLineCnt		;4 66 Store the scan line...wasting 1 leftover cycle
   CLC					   ;2 68
   BCC	PrintPlayer01	  ;2 71 always branch

 

 

 

For example, instead of using this for the alternate bat frame:

;Object #0E : State FF : Graphic
GfxBat2:
   .byte $01				 ;	   X
   .byte $80				 ;X
   .byte $01				 ;	   X
   .byte $80				 ;X
   .byte $3C				 ;  XXXX
   .byte $5A				 ; X XX X
   .byte $66				 ; XX  XX
   .byte $C3				 ;XX	XX
   .byte $81				 ;X	  X
   .byte $81				 ;X	  X
   .byte $81				 ;X	  X
   .byte $00				 ;bitmap end

 

...you can use this instead...

 

GfxBat2:;12 bytes, bat's 2nd bitmap (state $FF)
   .byte %00000000;
   .byte %00000000;
   .byte %00000000;
   .byte %00000000;
   .byte %00111100;  XXXX
   .byte %01011010; X XX X
   .byte %01100110; XX  XX
   .byte %11000011;XX	XX
   .byte %10000001;X	  X
   .byte %10000001;X	  X
   .byte %10000001;X	  X
   .byte GFX_DELIMITER;end

 

 

That would also make onscreen messages made with sprite gfx to be cleaner. No dots needed :)

Link to comment
Share on other sites

Indeed. Not to mention the maximum 255 game objects. I'm currently trying to figure out multicolor objects (w/o using ram), but placing 2 objects on top of one another is an alternative for background images.

 

A couple of bugfixes here...

 

The ball sprite would get stuck on walls occasionally (especially when "rolling" along one). This has been corrected by adding vertical-delay for it, and clearing the ball GFX at the end of the display.

 

Also, the cycle times listed in the kernel were incorrect during object display, these have been edited.

 

 

I also changed the kernel once again to eliminate the comparison at the end (CMP #$08). All vertical positions needed 8 pixels subtracted after this change, but the upshot (besides tighter code) is that the upper and lower lines of screens can be shared with the screen immediately following. Before, the scanline count had to be at least 16 for the lower line. Now it can match whatever you want to use :)

 

Example...before:

RoomXX:
  .byte $F1,$FF,$0F,$08
  .byte $31,$00,$00,$50
  .byte $F1,$FF,$FF,$10;<- note last value

RoomYY:
  .byte $F1,$FF,$FF,$08
  .byte $31,$00,$00,$50
  .byte $F1,$FF,$0F,$10;<- note last value

 

After:

RoomXX:
  .byte $F1,$FF,$0F,$08
  .byte $31,$00,$00,$50
RoomYY:
  .byte $F1,$FF,$FF,$08;(shared line for above)
  .byte $31,$00,$00,$50
  .byte $F1,$FF,$0F,$08

 

The program is still displaying 96 double-scanlines total. The 8 non-displayed lines are no longer needed. That lets you save an additional 4 bytes per screen (at least) when the upper line matches a lower line. The tighter code allowed me to bump up the timer by a notch, too.

 

NOTE: It's still possible to use the old room definitions (those ending with 16 scanlines). But now you have the option of putting the actual number of scanlines there for easier sharing of data. Just as before, any 2lk scanline counts above 96 ($60) will be ignored by the display.

 

 

Because of this change, I also needed to adjust the magnet routine a bit...so objects won't end up in hyperspace (the routine depended on those 8 non-displayed scanlines). I found that the object setup code was repositioning the objects for all 3 frames. Since the objects are only chosen ONCE for 3 frames, I cut this out of the other 2. The repositioning only needs to happen once new objects have been picked (i.e. at the end of CacheObjects). Notice the PrintDisplay subroutine...I just moved the initial lines to the bottom of CacheObjects. This picks up a good deal of cycle time for the other 2 frames. Repositioning of the ball sprite should remain on all 3, tho...since it uses this after colliding with stuff.

Anyway, moving those lines allowed me to reuse a ram variable (Obj1X) for the magnet routine...which is what I was getting at. It can be reused as a temp outside of the setup and magnet routines. Obj2X is no longer needed...so that saves an additional byte of ram, too! :D

 

 

Still looking for savings...

Edited by Nukey Shay
Link to comment
Share on other sites

Here's that change...

 

Also, a couple more bugs cropped up. The ripple effect on the ball has been fixed, and the sound effects have been fixed.

 

I also removed some more redundancy, rearranged the print order (VDELBL no longer needed), and bugfixed the magnet routine again (objects were still ending up on the wrong screen if you moved the magnet down into the border as it attracted an object down there).

 

I've got nearly a full scanline to use when the playfield GFX isn't being changed (i.e. the majority of the lines)...64 cycles minus the 3 needed to update GRP0...with all registers free. Possible repositioning for an intelligent flicker design? Hmm...

Ad_8k.zip

Link to comment
Share on other sites

Not so long as there is still free space/time during the actual display...and as long as it doesn't interfere with other things already in place (i.e. keeping it generic).

 

Another possibility is adding an additional pointer to the room variables. Since the scanline count doesn't need the 8 "invisible" lines at the bottom, a vertical pointer could keep track of your position in a very tall scrolling room. This would probably be a coded exception in the movement routine...and could just be passed to the Y register before the display executes if you happen to be in such a room.

Link to comment
Share on other sites

  • 3 months later...

Hey Nukey, I changed the graphics for room11 (I'm thinking of trying to do a totally different hack with the Adventure code) and I can't seem to make it work. What am I doing wrong? It changes to a dark room with surround activated and all I did was change graphics. I am attaching the code if you would like to take a look at it.

 

time103.txt

time103.bin

Edited by accousticguitar
Link to comment
Share on other sites

The room definition is in the wrong bank. All gfx MUST exist in the first bank, because that is the only place that they can be read for the display in this hack. Grab the Room11 tag and it's data and move it someplace in the first bank...there's plenty of space unused (everything from $10AB to $1C92 in that file).

So what happens is that the room is defined in the second bank at $F900. Because the display does not allow flipping back until the entire display has been drawn, this points at unused bytes in the first bank.

 

BTW another problem is that Rooms $0F and $10 have no bitmaps.

Link to comment
Share on other sites

That works much better, thanks. :)

BTW another problem is that Rooms $0F and $10 have no bitmaps.

Did I do that? :ponder: :D

 

Now I would like to add a game timer like the one in this thread:

http://www.atariage.com/forums/index.php?s...50191&st=75

I'm having trouble figuring out where to put it since the code has changed somewhat since then. Is it still viable?

Link to comment
Share on other sites

Not exactly as written, because that routine was intended to be dropped into the original kernel. The current one uses precise cycle counting leading into the kernel (to keep all available time available to the program subroutines)...so you'd need to move at least a couple of instructions above the INTIM loop so that there is time for the "are we inside the yellow castle?" exception.

 

An alternate method that requires no special setup would be to assign 4 static objects to the inside of the castle...or wherever you want them displayed (one for each digit that runs up to 99 minutes and 59 seconds). Use 4 bytes of RAM to hold the digits, and each digit can use the same state table. Digits 1 to 3 are already bitmapped, so you just need to add 7 more for the others. The current assembly has a lot of RAM free, so there is no problem there (but it might be possible to reduce this requirement to only 2 bytes). The only drawback would be increased flicker whenever they are displayed. Sound better?

Link to comment
Share on other sites

Here's that hack...

I burned 5 bytes of RAM here (I didn't want to mess with the operation of the low counter, so I added a seperate frame counter in addition to the 4 digit states). This new frame counter is bumped every display frame (the low counter is still bumped every 3rd frame). When 60 frames are reached, it's cleared and the last timer's state is bumped (and so on, up the list of timers). This area could definately be shortened, but I spread it out so you can see what is being done.

 

The timer is initialized along with objects upon a new game. Increments will not be done when the game select is shown or the game has been won...however, they WILL happen if the player has been eaten.

Adventure_Timer.zip

Link to comment
Share on other sites

I made some progress today using the original 8k code. I had a few hours to work on it so I did. What I am trying to do is make a game similar to Labyrinth for the Odyssey 2. I figured I could maybe do that by somehow changing the room bitmaps rapidly so that it would simulate the squares opening and closing to allow a path though. The problem I am running into right now is that the game will switch a few room bitmaps and then freeze. The weekend is almost over so I don't think I have time to try to figure out what is going on for a while. The game would probably look better using the new code though. I assume I am going to have the same problem switching bitmaps with the new code as I did with the old assembly. I used the movebat routine for timing then jumped to the randomizing routine which I modified to change the room bitmaps and then jumped back to the maingameloop.

 

I went with quadruple resolution in the old code making the room bitmaps 84 bytes each, but I figure I need only about 32 bitmaps so it might be doable. Anyway, the timer in the new code you posted looks fine to me. I think the bitmaps in the new code are going to run 64 bytes each.

 

Here is the assembly for the old code I was working on today. It's a mess: :)

 

time446.txt

 

time446.bin

Link to comment
Share on other sites

  • 1 year later...

I'm currently trying to figure out a use for the spare cycles in the kernel. Without burning ram, there's not enough time to update both sprites on every scanline and/or updating sprite colors for multicolor objects...but it's possible to use multicolor graphics for the playfield @ no cost of ram (I'm bugfixing that mode now...which can be toggled on or off in the assembly). The disadvantage would be that the data lines for room definitions would be 5 bytes instead of 4...and rooms could not share the same gfx if they were to be different colors. With a 16k build, it's not really a big concern ;)

post-222-129806237811_thumb.jpg

Link to comment
Share on other sites

Current version. The example picture below was produced by adding 7 bytes just below the first line of the yellow castle definition.

 

Room11: ;Yellow Castle (outside)
      .byte  $90,$F0|REV           ,$FE,$15,$08
;altered first byte of the above...then more values added for color:
      .byte $81,$71,$61,$51,$41,$33,$21

      .byte RC11,$30|REV           ,$03,$1F,$10
      .byte RC11,$30|REV           ,$03,$FF,$10
      .byte RC11,$30|REV           ,$00,$FF,$10
      .byte RC11,$30|REV           ,$00,$3F,$10
      .byte RC11,$30|REV           ,$00,$00,$10
      .byte RC11,$F0|REV           ,$FF,$0F,$08

 

 

 

Aside from the intro color given as the first byte in the definition, 7 additional colors is maximum for that line because the double scanline count is 8. In this manner, you don't need to define all the bytes if you are just changing colors...just define as many color values you want as there are scanlines.

 

 

So that the bottom of screen data does not accidentally grab color values from a following screen definition below it, it's suggested that you begin each screen definition using an EVEN value (bit0 = 0) for the first color value...hence, the $90 above. You'd need to put a trailing zero in the definition if it was followed by some other non-screen data (such as the top line of a sprite that uses the low bit).

 

 

EDIT: bugfix...

The objects were being sent 1 scanline too soon - corrected.

I saved another 2 cycles in the kernel, so I used them to restore all of the original game Y co-ordinates. No chance of the magnet accidentally dragging something into hyperspace (Y=negative) now.

post-222-129807586889_thumb.jpg

Edited by Nukey Shay
Link to comment
Share on other sites

Options to use either kernel in banks added.

 

In this manner, you can put multicolor rooms in seperate banks than monocolor rooms. This allows byte sharing of different color rooms (and saves 1 byte per data line regardless) in the bank that is using the monocolor kernel.

 

Just use either Multicolor_Display_Kernel OR Monocolor_Display_Kernel as the first line for that bank. Just as before, any immobile sprite gfx for that room must be in the same bank as the room definition.

Edited by Nukey Shay
Link to comment
Share on other sites

These edits are making playfield definitions very large (so large in the Multicolor kernel that it's possible it wouldn't all fit on a page for 1 screen...if you are recoloring lines all over the place and using very narrow strips of playfield). So I updated the program to avoid page breaks in it's spare time and bump to the next page :)

 

Notes:

 

Data for playfields are normally not allowed to cross page boundries. However, the monocolor playfield kernel will automatically skip the remainder of a bank if there is not at least 4 bytes remaining in the bank before a boundry will be encountered, and the pointers will be adjusted to look in the next bank for the remainder of screen data.

 

ex. (address/bytes)

$11FA: .byte PF0,PF1,PF2,#scanlines

$11FD: values here are irrelevant, because the end of page is 3 bytes away

$1200: .byte PF0,PF1,PF2,#scanlines (screen definition continues...)

 

 

The Multicolor playfield kernel will ONLY check for such occurrances IF the color data has just been read. So you should fill the remainder of the bank with color data and set the low bit in each to execute the subroutine to check for an upcoming page break.

 

ex. (address/bytes)

$11FA: .byte COLOR,PF0,PF1,PF2,#scanlines

setting the low bit in the remaining 2 bytes to call recolor routine

$11FE: .byte colorval+1,colorval+1

screen definition continues...

$1200: .byte COLOR,PF0,PF1,PF2,#scanlines ;for a new line of playfield

-OR-

$1200: .byte colorval+1,colorval+1,... ;to continue recoloring lines

 

 

These changes do not concern you at all if you are keeping careful track of page breaks on your own and have no definitions that need to cross them...but it's here if you need it.

If you -haven't- been keeping track of page breaks at all, you'll notice some more-apparent screen glitches when it does...and be able to spot where your definitions cross them that much easier to add ALIGN statements into it.

 

 

 

Example screenshot below (thx keithbk) uses 268 bytes to define the playfield. No glitches for crossing the page, because the last byte before the imbedded page break is a color adjustment value :)

post-222-129843724281_thumb.jpg

Edited by Nukey Shay
Link to comment
Share on other sites

Current changes:

 

Ball (when on upper scanline) and Object2 (when on left border) glitches fixed.

 

If playfield data is crossing a page for the monocolor kernel, it's no longer necessary to waste an even 4 bytes at the end of the page (you'd still need to waste 1 to 3 bytes tho...if that is all that remains for that page). If scanline info exists at $xxFF, the page will automatically be adjusted (no more wasting those 4 bytes!).

 

Speedier routines have been made for the object cache and portcullis sections. In addition, there are "unroll" options added for the collision and repositioning subroutines...which can pick up a fair amount of machine cycles if your game is running low / glitching. I figured as long as the unused romspace held out, might as well use dedicated routines. Just turn the options off if you run low of space for adding rooms, etc.

 

The "Null" object has now been put first in the list. Doing so simplified some things...and also made a new constant (LastPassNumber) available. By selecting which object number you want to be last for the ones you can "walk behind"...just edit it. For example, to walk behind gates when they are closed, set LastPassNumber equal to BGateNumber :) It still won't let you in if the gate is closed. This option makes adding non-collision static objects a breeze...just stick 'em in between the null and surround objects if you don't want them to collide, or after the surround object if you do.

 

An additional byte of ram has been saved by merging the bat's animation state with it's "fed-up" state. This worked perfectly, because the animation only used 3 bits and fed-up only used 4 bits :) Bit3 is still unused if you want to expand the animation to use a couple extra frames.

Adventure16k.zip

Edited by Nukey Shay
Link to comment
Share on other sites

To be honest...I like the "walk behind decorative objects" aspect better. Dunno if it can be hacked to do that with the playfield mid-screen :(

 

Ought to be able to put all gates into 1 byte...since they really only need 2 bits each (closed, opening, open, closing). This probably would have worked better if the original game featured 4 castles instead of 3 ;)

 

There's still ram to be saved.

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