Jump to content
IGNORED

making bowling 4k


Wickeycolumbus

Recommended Posts

I recently disassembled bowling (2k) and am interested in hacking the graphics and gameplay, but want at least 4k of space.

 

How can I convert the rom from 2k to 4k?

 

After disassembly, move the last 4 bytes of it 2k forward. This is accomplished just by adding an ORG $xFFC instruction above those 4 bytes (where "x" is the same digit used at the top ORG in the disassembly). To move things around, however, you'll need to have the program reverse-engineered far enough so that all of the vectors (values used for indirect loading) pointing to existing data have been converted into labels that reflect the actual spots in the disassembly.

 

For a small 2k game, it shouldn't take too long to create one if it hasn't been done already.

Link to comment
Share on other sites

Already had one that I forgot about :P

 

At the end of the file, just change this...

	   .word START

LF7FE:
   .byte $40; | X	  | $F7FE
   .byte $80; |X	   | $F7FF

 

 

 

...to this:

	   ORG $FFFC
   .word START

LF7FE:
   .byte $40; | X	  | $F7FE
   .byte $80; |X	   | $F7FF

 

 

There was only 1 indirect instruction used in the program. It uses ram addresses $AA and $AB to store an address. Note how the data table LF7E8 uses label tags instead of values. After disassembly, this table was identified just by searching for all signs of indirect loading (just look for a left parenthesis character), and that happens with ram address $AA. So searching for where ram address $AA and $AB were stored to identified the correct label locations.

Bowling.zip

Link to comment
Share on other sites

Already had one that I forgot about :P

 

At the end of the file, just change this...

	   .word START

LF7FE:
   .byte $40; | X	  | $F7FE
   .byte $80; |X	   | $F7FF

 

 

 

...to this:

	   ORG $FFFC
   .word START

LF7FE:
   .byte $40; | X	  | $F7FE
   .byte $80; |X	   | $F7FF

 

 

There was only 1 indirect instruction used in the program. It uses ram addresses $AA and $AB to store an address. Note how the data table LF7E8 uses label tags instead of values. After disassembly, this table was identified just by searching for all signs of indirect loading (just look for a left parenthesis character), and that happens with ram address $AA. So searching for where ram address $AA and $AB were stored to identified the correct label locations.

 

wow, thanks, this helps alot. I am planning on adding a title screen and improved graphis. Thank you very much!

Link to comment
Share on other sites

No prob. Keep in mind that you'll need to keep addresses of the bowler bitmaps all on the same "page" in memory. Tho the address loading routine could be altered easily to make it possible for them to exist on multiple pages...just add instructions to save the MSB to ram location $AB.

You'll also need to insure that some tables used in time-critical areas do not cross page boundries. The long gfx bitmap holding digit pixels should not be allowed to cross a page boundry, for example.

You've got 2k of free space, so just add ORG instructions to easily insure this.

 

Also, I hope that you are not planning on trying to make pin-shaped objects. Since the kernal is using a single 8-bit sprite to do this, it wouldn't be possible without a kernal rewrite. It's easily possible to change the number of frames used to animate the bowler tho (for smoother movement). Just change the routine that updates ram $8D to happen quicker rather than the 16 frames it currently uses...

 

	   lda	$80  ;load frame counter
   and	#$0F ;check the contents of the lower nybble
   bne	LF497;branch if anything but 0 (15 of 16 frames)
LF495:
   inc	$8D  ;Otherwise, bump the animation frame #
LF497:
   jmp	LF62B;...program continues

 

Without additional work, you could use AND values #$07, #$03, or #$01 to bump the animation frame following (7 of 8), (3 of 4), or (1 of 2) frames respectively.

 

 

Then increase the number of addresses held in table LF7E8. You'll need to adjust the value wherever $8D is checked to be the same number of bytes. Like this one...

 

LF354:
   ldy	$8D  ;check the animation frame #
   cpy	#$05 ;on the last frame?
   beq	LF378;branch if so
   bcs	LF3D8;branch if already reached before

 

So if the table holds 9 bitmaps, you'd need to cpy #$09...for example.

Edited by Nukey Shay
Link to comment
Share on other sites

I havent studdied the code very much, but would it be ok If i made a different kernel for the title screen before Label Start? would I have to modify anything. Keep in mind, i am not a very good 2600 programmer yet, I am using this project to get better at programming so i can work towards making my own game.

 

Thank you, you have been a lot of help!

Link to comment
Share on other sites

I havent studdied the code very much, but would it be ok If i made a different kernel for the title screen before Label Start? would I have to modify anything. Keep in mind, i am not a very good 2600 programmer yet, I am using this project to get better at programming so i can work towards making my own game.

 

Thank you, you have been a lot of help!

 

The start address performs a ram clear loop (it fills all ram with the value of 0). This must happen in all programs, because the hardware powers up in an unknown state. The graphics registers, sound registers, etc. all hold gibberish...basically why you see/hear this gibberish repeated over and over when powering up without a cartridge plugged in, there's no program present to instruct it otherwise.

A better solution is to use an unused ram location to hold a value to instruct the program to skip the normal display, and execute a different one instead. At the end of this other display, jump back to where the regular one ends. That way, you don't need to worry about timing issues between the 2 displays (if it doesn't match, the screen would roll on the frame that the program no longer branches to this custom display). Bowling has a bit of ram untouched. Looks to be addresses $F5 up to $FD (with $FE and $FF used by the program stack...holding a return address wherever a JSR executes). That should be plenty to use for title screen variables.

So the easy solution is to throw a value into one of these ram locations just following the ram clear loop, and clear the value after displaying the last frame of the title display. Let the program decide which of the displays to use when the screen is being drawn, by checking the ram location for the value you used. If present, jump to a different routine that draws a different screen. $80 is used as the frame counter in the game, so you could reuse this ram location in your display...checking for a specific value for when to clear the "display title screen" variable.

Link to comment
Share on other sites

I havent studdied the code very much, but would it be ok If i made a different kernel for the title screen before Label Start? would I have to modify anything. Keep in mind, i am not a very good 2600 programmer yet, I am using this project to get better at programming so i can work towards making my own game.

 

Thank you, you have been a lot of help!

 

The start address performs a ram clear loop (it fills all ram with the value of 0). This must happen in all programs, because the hardware powers up in an unknown state. The graphics registers, sound registers, etc. all hold gibberish...basically why you see/hear this gibberish repeated over and over when powering up without a cartridge plugged in, there's no program present to instruct it otherwise.

A better solution is to use an unused ram location to hold a value to instruct the program to skip the normal display, and execute a different one instead. At the end of this other display, jump back to where the regular one ends. That way, you don't need to worry about timing issues between the 2 displays (if it doesn't match, the screen would roll on the frame that the program no longer branches to this custom display). Bowling has a bit of ram untouched. Looks to be addresses $F5 up to $FD (with $FE and $FF used by the program stack...holding a return address wherever a JSR executes). That should be plenty to use for title screen variables.

So the easy solution is to throw a value into one of these ram locations just following the ram clear loop, and clear the value after displaying the last frame of the title display. Let the program decide which of the displays to use when the screen is being drawn, by checking the ram location for the value you used. If present, jump to a different routine that draws a different screen. $80 is used as the frame counter in the game, so you could reuse this ram location in your display...checking for a specific value for when to clear the "display title screen" variable.

 

So I have 9 bytes of ram to play around with? sweet, i was assuming that all ram was used up in the actual game, that is why i would make a different kernel just for the title screen and then clear all the variables and tia registers after the joystick button is pressed, and then it would go to the real game.

 

Also, I noticed that player graphics for the bowler are upside down (starting at LF7B8), why is that?

Link to comment
Share on other sites

Same principle...clear the ram value used for the "display title screen?" variable when INPT4 shows a trigger press. You only need 1 bit...and you could merge INPT4 + INPT5 (via AND) to check both triggers instead if you wanted. Might be a good idea to also check the console switches by reading SWCHB (so the player doesn't need to be informed about how to skip ahead).

The ram addresses $FE-$FF are also free when a subroutine doesn't have it's return address occupying the stack. The stack builds itself downward using the current stack pointer as subroutines are executed via JSR instructions, RTS pulls off the last stored return address (2 bytes) and bumps the stack pointer back up. The stack pointer keeps track (that's why it's normally reset to #$FF following ram clear...indicating the first ram location to use for anything that affects the stack).

 

 

And most games use reversed GFX...it's just simpler to start drawing the image from it's last value (the image start address + the value of the index register), and decrement the counter until it rolls over. This rollover detection doesn't require a compare instruction that way...just decrement the index register and check it with BPL (loop back for the next line unless the counter has rolled negative).

 

Because multiple images exist for the player's sprite, the Y register is often used in games. Initially load Y with the number of scanlines the image requires, load the gfx using an indirect-Y instruction, DEY to bump the index down, then loop unless it fell negative.

Link to comment
Share on other sites

Here's a sample assembly that does the above. A different display is shown on powerup, exited by any change of the triggers or console switches. I didn't mess with timing, so the demo display only allows a limited number of scanlines so that both displays end w/262 shown (no need to use a "show title screen?" variable)...but more scanlines could be drawn if you want to use seperate timer loops. There's plenty of time to set up variables (location shown), and all ram can be used apart from address $81 (stores a copy of the console switch status). The bowler bitmaps have been moved to a seperate page for easy editing.

Bowling4k.zip

Link to comment
Share on other sites

Here's a sample assembly that does the above. A different display is shown on powerup, exited by any change of the triggers or console switches. I didn't mess with timing, so the demo display only allows a limited number of scanlines so that both displays end w/262 shown (no need to use a "show title screen?" variable)...but more scanlines could be drawn if you want to use seperate timer loops. There's plenty of time to set up variables (location shown), and all ram can be used apart from address $81 (stores a copy of the console switch status). The bowler bitmaps have been moved to a seperate page for easy editing.

 

I noticed that when you pull the reset switch on the title screen, it starts to play the game. how can this be fixed?

Link to comment
Share on other sites

The easy way would be to go back to the original idea...ignore the switches on the title screen by commenting off these lines...

;...below label LF005
;	   lda	SWCHB				  ;3
;	   sta	$81					;3 save current console switches

 

...and...

 

;...below label FillScreen
;	   lda	SWCHB				  ;3 check console switches
;	   cmp	$81					;3 compare with previous
;	   bne	Exit				   ;2 branch if different (and exit)
;	   sta	$81					;3 ...otherwise, store to ram for next time

Just throw a semicolon at the front of each line to make Dasm ignore them.

 

 

 

But those lines do provide a quick way to start a game. I don't see a problem with it. You can exit the display by altering any switch or joystick trigger. You could also have it exit if any stick is moved (SWCHA = $FF when neither is moved).

Alternately, you could perform an AND #FC after loading SWCHB in both code segments...to have it ignore reset and game select switch presses....

 

;...below label LF005
   lda	SWCHB				  ;3
   and	#$FC				   ;2 ignore reset & select
   sta	$81					;3 save current console switches

 

...and...

 

;...below label FillScreen
   lda	SWCHB				  ;3 check console switches
   and	#$FC				   ;2 ignore reset & select
   cmp	$81					;3 compare with previous
   bne	Exit				   ;2 branch if different (and exit)
   sta	$81					;3 ...otherwise, store to ram for next time

 

The way that works is the AND instruction only keeps the 6 left bits (and throws away bit 1 and 0...which correspond to the select and reset switches respectively).

Link to comment
Share on other sites

The ball is made of the ball sprite and sprite0's missile. Changing the pin color automatically changes the missile part of the ball, because it's shared. So just throw a PF color store in there too...

;	   lda	$CF					;3 (original)
   lda	#$0F				   ;2 load white color
   sta	COLUP0				 ;3 store to sprite0/missile0
   sta	COLUPF				 ;3 ...also store to the ball

Don't forget to use the # symbol when loading values directly.

 

 

BTW you bumped up the number of scanlines on the title screen. If you do this, you should use seperate timer loops so that the screens are always at 262. Lower the value given to TIM64T after drawing the display (to #$38), and put 2 more lines in the exit routine...

Exit:
   lda	#$38				   ;2 store time to waste (seperate from title screen)
   sta	TIM64T				 ;4
   jmp	LF2A7				  ;3 finally, go to the game program

 

In this case, it didn't matter (because both values should be at $38 after adding that single scanline). But if you want to give Y a higher value than #$A9 to be able to draw more lines in the title display, you'll need to reduce the first value given to TIM64T to bring the scanline count back down to 262. The exit routine's value might not always match, so you'll be able to adjust it independantly. You need at least a few clicks so that the program has time to initialize the variables for the ingame screen.

 

The switch to display scanline count is -n in Z26...as in Z26 Bowling -n. If a game does not hold a steady count, it could cause a television display to jump when the count changes.

Link to comment
Share on other sites

I fixed the white ball, thanks for telling me how, i would have never been able to figure it out. I know the answer to this is probably a no, but is there a way other than a flicker to make the ball black and the pins white?

 

As for the title screen, i needed it to be full resolution for the screen that i wanted, but i will fiddle around with that later on.

 

I noticed that all of the instructions have comments with the number of machine cycles, is there a switch in distella that does this for you?

 

Thank you

Bowling24k.bin

Link to comment
Share on other sites

The option is enabled with the -s switch during disassembly. I always use the string -pafs (plus the -c switch, if I have a config file).

p = show processor type at the top of the disassembly

a = omit "A" when instruction acts on the accumulator

s = show cycle count for each instruction

f = force correct address length (show MSB for ram access)

c = use configuration file to specify code/data/gfx areas

 

Distella displays the switches when you use it alone, as a help screen.

If you disassemble without using p, a, or f...the disassembly could generate errors when you use Dasm to reassemble it. So it's always good practice to at least use those 3 switches.

 

When hacking display kernals like this, the s switch comes in handy (so you know exactly the number of cycles used in any given area without having to figure them out).

 

Changes to make the ball a standalone object:

The easy way is to use the native horizontal motion register so that the ball's position can be adjusted slightly after resizing it (NOTE: this will cause the "comb" to be displayed...other edits can be made to eliminate it if you find it to be unacceptable).

 

HMBL isn't defined, so add that first to the top...

HMBL	=  $24

 

 

Next, change the color stores slightly so that you can use independant colors. Also, throw in the HMCLR instruction to clear all motion prior to drawing the sprites...

;	   lda	$CF				;3
   lda	#$0F				  ;2 load pin color
   sta	COLUP0				;3 store to sprite0/missile0
   lda	#$00				  ;2 load ball color
   sta	COLUPF				;3 store to the ball
   sta	HMCLR				 ;3 clear horizontal motion

 

 

When drawing the sprites, no playfield pixels are being used...so you don't need to worry about funky CTRLPF values (CTRLPF is also used to resize the ball sprite). Luckily, enough cycle time remains to squeeze in a seperate table load (for reading where to position the sprite). So apply HMOVE to the code, and add some lines to read and adjust the ball position...

LF233:
   sta	WSYNC				 ;3
   sta	HMOVE				 ;3 added line
   sta	GRP1				  ;3
   ldy	$D5				;3
   lda	LF72E,Y			;4
   sta	ENABL				 ;3
;	   asl						  ;2 (no longer using the missile)
;	   sta	ENAM0				 ;3 ""
   sta	CTRLPF				;3 added lines...
   lda	Motion,Y			  ;4 ""
   sta	HMBL				  ;3 ""
;original code continues...
   cpx	#$22				  ;2
   bcs	LF249				 ;2
   lda	$DA,X				 ;4
   sta	GRP0				  ;3

 

 

A new table name (Motion) was used above...so edit the data area a bit to add it. Also, the missile is no longer used, so those data lines can be changed to just hold the ball sprite width and where to enable it...

 

LF72E:
;original lines...
;	   .byte $02; |	  X | $F72E bottom
;	   .byte $03; |	  XX| $F72F
;	   .byte $03; |	  XX| $F730
;	   .byte $03; |	  XX| $F731
;	   .byte $02; |	  X | $F732 top
;	   .byte $00; |		| $F733
;new lines...
   .byte $12; |   x  X | $F72E bottom
   .byte $22; |  x   X | $F72F
   .byte $22; |  x   X | $F730
   .byte $22; |  x   X | $F731
   .byte $12; |   x  X | $F732 top
Motion:;ball sprite position adjustment
   .byte $00; |		| $F72E bottom (shared)
   .byte $F0; |XXXX	| $F72F move right
   .byte $00; |		| $F730
   .byte $00; |		| $F731
   .byte $10; |   X	| $F732	 (move left)
   .byte $00; |		| $F733 top

 

Notice that I placed small x's in the first table to help show you the changes to the upper nybble (now used to set the ball sprite width).

The "Motion" table is applied after each line is drawn, so it's offset by 1 byte. The $10 value moves the sprite to the left so that the wider ball is centered with the line above it on the screen...and $F0 shifts it back to the right.

 

The data area has been increased in size, so the ORG definition above it needs to be edited too...

;	   ORG $FF51
;new origin...
   ORG $FF4C

 

 

There ya go :)

Bowling4k.zip

post-222-1200171958_thumb.jpg

Edited by Nukey Shay
Link to comment
Share on other sites

Thanks alot, i did not try it yet, but what is up with the black area behind the bowler? can they be removed?

 

The black area behind the bowler is sometimes called the "comb". There is no way to avoid having a black line at the left of the screen on the bottom scan line of the ball (since its sprite has to move rightward). The code as written throws the black line on every scan line where the bottom of the ball could (at least theoretically) be. Changing it to eliminate all but the scan lines where the ball appears probably wouldn't be too hard. Changing it to appear on the ball's bottom scan line only might be tougher. If the black lines track the ball, you could pretend they were "aiming assistance" or somesuch nonsense.

 

Another alternative, although you may want to change the ball color in this case, would be to set the background color to black for every other scan line. That might be an interesting effect. Not sure if there are enough cycles for that.

Edited by supercat
Link to comment
Share on other sites

The black lines is the "comb" I mentioned. This is a direct result of using HMOVE to shift sprite positions. There are a number of ways to hide it...you can:

 

...Reset the background color to black (easiest)

 

...Use #$30 and store that to PF0 (ok if the ball color is going to be black anyway...and so long as the playfield is reflected and no pixels are otherwise needed)

 

...Perform an HMOVE on every single line (more difficult...especially since the HMBL doesn't take effect right away in this example).

 

...Use cycle-specific timing in the display to avoid using HMOVE (can't help you with that one).

 

Of those, the 2nd solution should work OK if you don't mind the bowling area to be narrower than the top (because the playfield pixels are used to draw the score - which uses a changing color). To make the screen the same width on all lines, you could just edit the score color to always be black.

 

The easiest way will always be the first solution...reset the background color to be black for the entire screen (HMOVE lines are always black, and this effectively hides them if there are no foreground pixels being drawn). Of course, since the ball shares the foreground color you'd need to use a color other than black for the ball.

 

 

The "comb" generally doesn't bother those that are familiar with the 2600 library. We grew up looking at it in many games.

Edited by Nukey Shay
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...