Jump to content
IGNORED

My first Homebrew: Vong


Wickeycolumbus

Recommended Posts

This is my first homebrew for the 2600 written in assembly, and I am very proud of it, since I am only 14 years old. The name of it is "Vong" for Vertical pong. It plays like pong, but instead of the ball going side to side, it goes up and down, hence the name Vong. There is some AI, but it is not too good. I plan to change all of the colors to black and white, like the original pong. There is no scoring currently, but there will be in a future update. I would also like to add paddle support, and a 2 player option.

 

Enjoy!

vong20080328.zip

Link to comment
Share on other sites

This is my first homebrew for the 2600 written in assembly, and I am very proud of it, since I am only 14 years old. The name of it is "Vong" for Vertical pong. It plays like pong, but instead of the ball going side to side, it goes up and down, hence the name Vong. There is some AI, but it is not too good. I plan to change all of the colors to black and white, like the original pong. There is no scoring currently, but there will be in a future update. I would also like to add paddle support, and a 2 player option.

 

Enjoy!

 

It's looking great so far! I look forward to see how this project progresses. Cheers.

Link to comment
Share on other sites

This is my first homebrew for the 2600 written in assembly, and I am very proud of it, since I am only 14 years old. The name of it is "Vong" for Vertical pong. It plays like pong, but instead of the ball going side to side, it goes up and down, hence the name Vong. There is some AI, but it is not too good. I plan to change all of the colors to black and white, like the original pong. There is no scoring currently, but there will be in a future update. I would also like to add paddle support, and a 2 player option.

 

Enjoy!

 

Nice! Very impressive for your age!

Link to comment
Share on other sites

I posted sample paddle reading code here.

 

thank you for the link. Are those macros a part of macro.h?

 

and I also have some questions about the code.

 

		MAC READ_PADDLE_1
	lda INPT0	   ; 3   - always 9
	bpl .save	   ; 2 3
	.byte $2d	   ; 4 0   What does this do?
.save   sty Paddle1	 ; 0 3  To read the paddle you read Paddle1? (lda Paddle1)
	ENDM

 

You say before you posted this code that Y holds the current scanline. Do you have to put the current scanline into Y, or does that just happen after you use the macro?

 

Thanks.

Link to comment
Share on other sites

	.byte $2d	; 4 0   What does this do?

This is the byte code for the "BIT absolute" instruction ($2c, NOT $2d!). The trick is, that it uses the two next bytes (sty Paddle1) as argument to read from, effectively doing nothing and just skipping the next two bytes with constant(!) cycle count. It takes 4 cycles to execute.

 

	.save   sty Paddle1; 0 3  To read the paddle you read Paddle1?

No, that's just the name of a RAM variable you write to as long as the paddle's capacitor hasn't loaded. INPT0 is reading the first paddle (BTW: actually that's paddle 0, not 1).

 

Do you have to put the current scanline into Y, or does that just happen after you use the macro?

Yes. Though a lot of kernels use Y as a global scanline count variable, so then you have the value required there already and save some cycles.

 

The code was originally discussed here.

Edited by Thomas Jentzsch
Link to comment
Share on other sites

	.byte $2d; 4 0   What does this do?

This is the byte code for the "BIT absolute" instruction ($2c, NOT $2d!). The trick is, that it uses the two next bytes (sty Paddle1) as argument to read from, effectively doing nothing and just skipping the next two bytes with constant(!) cycle count. It takes 4 cycles to execute.

 

	.save   sty Paddle1; 0 3  To read the paddle you read Paddle1?

No, that's just the name of a RAM variable you write to as long as the paddle's capacitor hasn't loaded. INPT0 is reading the first paddle (BTW: actually that's paddle 0, not 1).

 

Do you have to put the current scanline into Y, or does that just happen after you use the macro?

Yes. Though a lot of kernels use Y as a global scanline count variable, so then you have the value required there already and save some cycles.

 

The code was originally discussed here.

 

 

Ok, so I run the macro, with the current scanline count in Y. I still dont really get how you read it tho...

 

EDIT: I clicked the link, and it was dead.

 

EDIT: I clicked it again and it worked.

 

EDIT: So would something like this work?

 

	sta HMCLR
lda INPT0
cmp shadowINPT0
bmi paddleminus
bpl  paddleplus

paddleminus
lda #%00010000
sta HMP1
sta shadowHMP1
jmp regular

paddleplus
lda #%11110000
sta HMP1
sta shadowHMP1

regular

  ;on with the rest of the program

Edited by Wickeycolumbus
Link to comment
Share on other sites

EDIT: So would something like this work?

I suppose you want to execute the code once each frame right? Won't work.

 

The problem is, that INPT0 does NOT contain a variable value. It either contains 0 or 1 (at bit 7, the other bits are unused here), so your code has to measure how long it takes to switch that bit. And this is usually done during the display kernel.

 

BTW: After posting my answer here yesterday, I found an even faster (though a bit more complicated) solution:

  bit INPT0   ; 3
 bmi .save+1 ; 2/3
.save:
 sty paddle  ; 3/2	paddle has to be at the address of an 1 byte, 2 cycles opcode, e.g. $ea (NOP)

Total: 8 cycles, 1 cycle saved :)

Edited by Thomas Jentzsch
Link to comment
Share on other sites

EDIT: So would something like this work?

I suppose you want to execute the code once each frame right? Won't work.

 

The problem is, that INPT0 does NOT contain a variable value. It either contains 0 or 1 (at bit 7, the other bits are unused here), so your code has to measure how long it takes to switch that bit. And this is usually done during the display kernel.

 

BTW: After posting my answer here yesterday, I found an even faster (though a bit more complicated) solution:

  bit INPT0  ; 3
 bmi .save+1; 2/3
.save:
 sty paddle ; 3/2	paddle has to be at the address of an 1 byte, 2 cycles opcode, e.g. $ea (NOP)

Total: 8 cycles, 1 cycle saved :)

 

So paddle is just any RAM address and it contains the number of scanlines that it took to get a 1 from INPT0? Dont you have to do something before you execute that code to instruct the paddle to start charging the capicitor?

Link to comment
Share on other sites

Yep - done via VBLANK. At the MiniDig you can find the Stella Programmer's Guide which has this on page 11:

 

12.1 Dumped Input Ports (INPT0 thru INPT3)

These four ports are used to read up to four paddle controllers. Each paddle controller contains an adjustable pot controlled by the knob on the controller. The output of the pot is used to charge a capacitor in the console, and when the capacitor is charged the input port goes HI. The microprocessor discharges this capacitor by writing a "1" to D7 of VBLANK then measures the time it takes to detect a logic one at that port. This information can be used to position objects on the screen based on the position of the knob on the paddle controller.

 

The D7 refers to a specific bit, whenever ordered as 76543210. $80 has bit 7 turned on. $82 has bits 7 and 1 turned on. The detail for VBLANK can be found on page 38:

VBLANK

This address controls vertical blank and the latches and dumping transistors on the input

ports by writing into bits D7, D6 and D1 of the VBLANK register.

 

D1 [ 1 = start vert. blank, 0 = stop vert. blank]

D6 [ 1 = Enable I4 I5 latches, 0 = disable I4 I5 latches]

D7 [ 1 = dump I6I1I2I3 ports to ground, 0 = remove dump path to ground]

Note : Disable latches (D6 = 0) also resets latches to logic true

 

The code from my demo:

VerticalBlank:
	lda #$82
	sta WSYNC
	sta VSYNC		; 3	start vertical sync, D1=1
	sta VBLANK	   ; 3  6 start vertical blank and dump paddles to ground

...

ldx Paddles2Read
lda #153	  ; prep paddle results with highest possible value
sta Paddle1,x ; our initial paddle results will be 1-153
sta Paddle3,x ; and will be adjusted to 0-152 in overscan
lda #0

VblankWait
	lda INTIM
	bpl VblankWait

	sta WSYNC
	sta HMCLR		; clear hmoves for next time around
	stx VBLANK	   ; turn on video output & remove paddle dump to ground

 

The lda #0 just before VblankWait was supposed to be ldx #0 so that the stx VLBANK would be zeroing out all bits. However X can only have a value of 0 or 1, which is stored in bit 0 - lucky for me, as seen in the bit from page 38, VBLANK only cares about what's in bits 7, 6 and 1.

Link to comment
Share on other sites

The problem is, that INPT0 does NOT contain a variable value. It either contains 0 or 1 (at bit 7, the other bits are unused here), so your code has to measure how long it takes to switch that bit. And this is usually done during the display kernel.

 

Atari implemented their paddles the cheapest way possible. Discharge a capacitor, release it, charge it through a variable resistance, and time how long it takes to reach a certain threshold. That time will be directly proportional to the variable resistance; on the 2600, it's designed to vary from almost nothing to something over a full frame (though I don't think any games use the latter part of the range).

 

One approach which can be handy in certain types of kernels is to vary the time that you release the cap, so that it will reach the threshold within a fairly narrow range of scan lines. It's only possible to read one paddle per frame with this approach, but it's possible to read paddles with single-line resolution even if much of the screen has large groups of lines with no spare cycles.

 

For example, if your screen is divided into 10-line blocks, and you only have a few cycles at the end of each such block, all you need to squeeze in once every ten lines is something like (preferably do this just before a loop branch so you won't have to copy much code)

  ldx paddlectr
 dex
 bne nohit
 stx.w VBLANK
 .. one copy of succeeding code
nohit:
 stx lctr
 .. another copy of succeeding code

Ten cycles, every ten scan lines. Some time later on the screen, you'll need a loop which checks the paddle every line, and you'll have to adjust the starting value of paddlectr so that the paddle is expected to time out around the middle of that loop.

 

A somewhat tricky approach, but it can yield very nice results.

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