Jump to content
IGNORED

7800basic beta, the release thread


RevEng

Recommended Posts

 

The point is, for those that chose not to implement POKEY and stick with TIA sound, for whatever reason, don't shy away from 7800 game development. :)

 

That's a good point, Trebor. So far, I've got some (pretty lovely) sprites moving around the screen doing things and some TIA sounds to accompany them. That I can even do that at all with a 7800 is a minor miracle to me--and it's a big deal that RevEng has lowered the bar to let others in. (I'm dying to really get stuck into assembly, but there's that time problem again....) So, no, TIA isn't to be knocked or mocked. It has served us well for decades now and will continue to be of use.

 

 

Yep. Have to finish 2 Atari 2600 projects before I can dive in. Then it's all 7800bas, Sega Genesis and Jaguar in assembly. Totally understand why there's more lurkers than definite projects :)

Ahh, now, I was wondering when you'd put your hand to the 7800. :) Looking forward to your work here; doubtless it will be very impressive!

Link to comment
Share on other sites

Multiple replies inline, to avoid spamming the thread. :)

 

Will it be able to do speech synthesis, or is there a different module for the 7800 for that? Or would you just use an AtariVox?

AtariVox support is forthcoming, both for saving and voice. TBH this is pretty trivial since I already have wrapper code for bB, but I won't have proper hobby coding time for the next month or so.

 

You can use Pokey or TIA for sample playback, but I'm not planning to write code for this at this time, so you'd need write the code for twiddling the registers yourself. Maybe I'll support it some day, but it will be a long while. Sample playback is cool, but it will eat most of your available CPU time, and a good chunk of ROM.

 

 

TIA is definitely no POKEY. However, the audio in these two games is super impressive[...]

Yep, agreed those are stellar TIA works!

 

There's an art to tweaking TIA audio. Its my hope that with the sound effect driver in 7800basic that we'll someday get to a point where people share sounds. Anyway, I'll try to flesh out the "soundtest" sample at some point, so people can borrow from there and hit the ground running with sound effects.

 

My own feeling is that Pokey is best used if you plan to have complex music or in-game music. Pokey's noise capabilities overlap a fair bit with TIA's capabilities, and with the sound effect routine I've found that TIA's 2 channels is enough for even a fairly busy game.

 

There's no online video with a good chunk of the title screen music, but Man Goes Down is another excellent example of nicely done TIA music.

 

 

[...] So far, I've got some (pretty lovely) sprites moving around the screen doing things and some TIA sounds to accompany them. That I can even do that at all with a 7800 is a minor miracle to me--and it's a big deal that RevEng has lowered the bar to let others in. (I'm dying to really get stuck into assembly, but there's that time problem again....) [...]

Thanks, I'm glad you're getting use out of it! You should find that 7800basic can be a kind of gateway to assembly if you're interested in that path. You can try out small bits of assembly code within your games, without having to learn how to drive the 7800 at a lower level until if/when you're ready for it.

  • Like 3
Link to comment
Share on other sites

There's an art to tweaking TIA audio. Its my hope that with the sound effect driver in 7800basic that we'll someday get to a point where people share sounds.

:thumbsup:

 

It would be AMAZING to see the community share with and benefit one another in that way.

 

...Man Goes Down is another excellent example of nicely done TIA music.

 

How did I ever miss it? Super impressive! Again, don't sell that TIA short; MGD sounds beautiful.

 

 

There's no online video with a good chunk of the title screen music...

 

Let's fix that:

 

https://www.youtube.com/watch?v=PQPdbdQO7RY

  • Like 2
Link to comment
Share on other sites

So if the TIA can pull off those awesome sound fx and music examples (throwing in Princess Rescue as another example), why doesn't everyone do it? What is the trick to making it sound great?

 

Is it just happenstance, like these particular notes happen to sound good but most don't? Is there some trick to writing to TIA with raw ASM that gets more sounds out than BatariBasic or 7800basic can? Or is it a dithering trick of playing two sounds together or in rapid succession to give the illusion of another sound, sort of like how color dithering can imply more colors and shades than are actually there?

Link to comment
Share on other sites

So if the TIA can pull off those awesome sound fx and music examples (throwing in Princess Rescue as another example), why doesn't everyone do it? What is the trick to making it sound great?

Why doesn't everybody do it? Its a lot of work, and takes a bit of musical foundation, which not everybody has.

 

Start with reading do re bB, Eckhard's charts and Glen Saunders' charts for some basics.

 

My own modest successes have come from keeping a few principles/techniques in mind:

 

1) Short notes are better than long sustained ones. Its easier to hear note is out of tune when it's sustained. This will also let you play more notes, which makes it sound like there are more than 2 channels.

2) Be willing to transpose the song in order to move badly pitched notes to unused or less important note positions. People are better at telling sour notes in positions that are at an interval to the key that's a simple ratio, like octave, perfect 5th, and perfect 4th.

3) Forget key changes. This will mess up #2.

4) Use webtune to figure out the best relative pitches for the notes in your arrangement, instead of going for perfect pitch. Most people can't tell the difference, and the few with perfect pitch won't be very upset if your song uses relative pitch.

5) Limit the note range of your composition where possible. Keep it within a single octave or less. The larger the range the more likely you will be to hit sour notes. This will also help with #2 and #4.

6) Obviously with 2 channels you can't do chords proper, but limited power chords and other 2 note chords work fine.

7) Change the volume of the notes to give them an envelope. Notes turning on and off at full volume are nearly as jarring as out-of-pitch notes.

 

 

 

Is it just happenstance, like these particular notes happen to sound good but most don't? Is there some trick to writing to TIA with raw ASM that gets more sounds out than BatariBasic or 7800basic can? Or is it a dithering trick of playing two sounds together or in rapid succession to give the illusion of another sound, sort of like how color dithering can imply more colors and shades than are actually there?

Nada. Assembly language makes no difference here, unless you're looking to use softsynth with PWM. Not many games do this. Stella's Stocking is one. Pitfall 2 uses softsynth with hardware assistance, as does Stay Frosty 2. The tradeoff for softsynth being in tune is that the CPU is kept very busy, and it sounds a bit scratchy.

 

I experimented with varying between two nearby tones to try and correct the pitch. I've heard other programmers espouse the technique, but to my ears it doesn't work at all, and I don't have perfect pitch or golden ears. YMMV.

  • Like 2
Link to comment
Share on other sites

  • 2 weeks later...

I don't have any comparison images, and haven't found much in the way of comparison pictures elsewhere.

 

There's some sample screen shots in this thread for 160A+B and 320B+D. Jinks is another title that used 320B+D. For 320A+C see Tower Toppler.

 

For a first game you may want to stick with 160A+B. It's easier to steer and plan palettes for.

Link to comment
Share on other sites

The NES does 320, doesn't it?

 

No. 256(x240). Additionally, the SMS is 256(x192/224).

 

320 and higher (512) became "typical (for the time)" starting with the 16-bit era. ;)

 

Sega Genesis: 256x224, 320x224

Super Nintendo: 256x224, 512x448

 

Many game(')s (graphics) though still used the 256x224 resolution - especially for the SNES.

  • Like 1
Link to comment
Share on other sites

Doesn't the 7800 use 16-bit memory architecture like how the Intellivision does? Maybe I'm totally mistaken, but I was under the impression that it did, in which case I'm surprised that 7800basic apparently doesn't support any kind of 16-bit variables.

 

Also, if I understand things right, sprites in the 7800 are either 8x16 or 16x16, depending on the zone height, while background tiles and such are 8x16, 4x16, or 2x16, correct? Then how does the Adventurer example, using mode 160A, specify background tiles as 8x16 when the max should be 4x16?

Edited by Cybearg
Link to comment
Share on other sites

The 7800 uses the 8-bit 6502 CPU, as did the NES and several home computers of the time. 16 bits of address space (memory architecture) doesn't imply other non-memory related 16-bit operations in the CPU. The rule of thumb for determining the CPU "bit" size is to look at largest register size. FWIW, I don't believe there's any 8-bit CPU with only 8 bits of address space.

 

The 6502 can chain 8-bit operations together to do 16-bit adds and subtracts (and 24-bit, 32-bit, 40-bit, ..) but when it comes to other operations and comparisons its going to be more efficient for basic developers to implement situation-specific code.

 

A few things worth mentioning. 7800basic works with fixed point numbers, which are a form of 16-bit variable with a decimal point providing a finer range instead of a larger range. There are also the 24-bit score0 and score1 variables. Lastly, you can implement 16-bit addition/subtraction using if...then with CARRY.

 

The sprites are either 8 pixels tall or 16 pixels tall, depending on the zone height. They can range in size from 1x[character width] to 32x[character width]. (ie. up to 128 pixels wide for 160A) You can also stack sprites on top of each other for the appearance of taller sprites.

 

The Adventurer sample doubles the effective character width for character plotting by using set doublewide on.

  • Like 1
Link to comment
Share on other sites

I was surprised 7800basic is provided and developed freely.

 

I'm not surprised, but it never ceases to amaze me, how some pick up and make the most of provisions and tools available.

We have some talented developers already spending their available time working with the fantastic developmental assistance 7800basic provides.

 

A couple of examples:

http://atariage.com/forums/topic/229946-working-title-draker-quest/?do=findComment&comment=3075878

http://atariage.com/forums/topic/226443-adventure-demo-7800basic/

 

What's truly surprising, as well, is when others look to support the efforts of others and encourage the community while developing. There are some great assets to this community that are very much appreciated.

  • Like 3
Link to comment
Share on other sites

Thanks for the info, RevEng. I guess it baffles me to imagine that it can handle 128-pixel wide sprites since I'm used to the 2600's unlimited height but limited width for sprites, unless you just mean extending the width by stretching the sprite (which doesn't seem to be the case with Adventurer, where the sprites are clearly 16 unique pixels wide, albeit stretched, 2x1 pixels). Too bad there doesn't seem to be a good way to keep pixels at their normal 1x1 size with 160A--the only way is apparently with one of the 320 screen modes, and those seem to come with a bunch of strange limitations in other areas.

 

So if one wanted a 16-bit timer, for instance, you'd just have some branching logic that checks for carries and then increments the higher byte and the reverse for decrements? How would one then efficiently draw that 16-bit variable to the screen, particularly if one wanted to limit it to, say,10,000?

Link to comment
Share on other sites

No problem.

[...]unless you just mean extending the width by stretching the sprite (which doesn't seem to be the case with Adventurer, where the sprites are clearly 16 unique pixels wide, albeit stretched, 2x1 pixels).[...]

The 7800 can draw sprites that are literally 128 pixels wide in 160A. It draws the screen very differently than the 2600.

 

So if one wanted a 16-bit timer, for instance, you'd just have some branching logic that checks for carries and then increments the higher byte and the reverse for decrements? How would one then efficiently draw that 16-bit variable to the screen, particularly if one wanted to limit it to, say,10,000?

The 6502 supports BCD mode for variables which allow it to represent numbers in a way that's efficient for screen display. To do this in 7800basic just use the "dec" keyword in front of your variable additions/subtractions.

 

In BCD, one byte will represent 2 digits on screen. So for 10,000, you need 3 bytes. Not a big deal, since we have over 1.5k of RAM in the 7800 not being used by 7800basic.

 

If you want to limit the variable to 10,000 you want to stop incrementing it past 10000. Display is trivial...

  dim shipcount1=a : rem ##0000
  dim shipcount2=b : rem 00##00
  dim shipcount3=c : rem 0000##
  [misc game code goes here]
  if shipcount1=$10 && shipcount2=$00 && shipcount3=$00 then goto SkipShipScoreUpdate
    dec shipcount3+shipcount3+1
  if CARRY then dec shipcount2=shipcount2+1
  if CARRY then dec shipcount1=shipcount1+1
SkipShipScoreUpdate
  [more game code]
  plotvalue myfont 0 shipcount 6 0 0 : rem 6 is the number of digits in 100000
  • Like 1
Link to comment
Share on other sites

That's exciting to hear! Glad it's so easy. :D

 

In cases like Adventurer, why is the on-screen character "fat" compared to the raw .png that's plugged in? Is there any way to maintain the "proper" pixel ratios?

 

Because of the aspect ratio of 160, a single pixel is 2 wide and 1 tall. ;)

  • Like 2
Link to comment
Share on other sites

Ah, gotcha. So it's different with the 320 modes, then.

 

I seem to have come across a... bug? I was trying to add the ability to move from one screen to another in the Adventurer demo, like so:

	temp1 = screenindex / 4
	temp2 = screenindex & 3
    if joy0down then if ypos < 172 then ypos=ypos+1:herodir=0:goto doneherowalk else if temp1 < 3 then screenindex = screenindex + 4: ypos = 8: gosub setlevel: goto doneherowalk
    if joy0up then if ypos > 8 then ypos=ypos-1:herodir=2:goto doneherowalk else if temp1 > 0 then screenindex = screenindex - 4: ypos = 172: gosub setlevel: goto doneherowalk
	
	if joy0left  then if xpos > 8 then xpos=xpos-1:herodir=1:goto doneherowalk else if temp2 > 0 then screenindex = screenindex - 1: xpos = 152: gosub setlevel: goto doneherowalk
    if joy0right then if xpos < 152 then xpos=xpos+1:herodir=3:goto doneherowalk else if temp2 < 3 then screenindex = screenindex + 1: xpos = 8: gosub setlevel: goto doneherowalk
 

The compiler handles the joy0down line just fine, but the other lines start to cause errors in the assembly, presumably because the compiler is creating duplicate label names:

 

 

K:\7800b\samples\adventurer\adventurer.bas.asm (422): error: Label mismatch...

--> 0.skipelse 80d8
K:\7800b\samples\adventurer\adventurer.bas.asm (453): error: Label mismatch...
--> 0.skipelse 8108
K:\7800b\samples\adventurer\adventurer.bas.asm (481): error: Label mismatch...
--> 0.skipelse 8131
.L048 ;  if joy0down then if ypos  <  172 then ypos = ypos + 1 : herodir = 0 : goto doneherowalk else if temp1  <  3 then screenindex  =  screenindex  +  4 :  ypos  =  8 :  gosub setlevel :  goto doneherowalk

 lda #$20
 bit SWCHA
	BNE .skipL048
.condpart3
	LDA ypos
	CMP #172
     BCS .skip3then
.condpart4
	INC ypos
	LDA #0
	STA herodir
 jmp .doneherowalk
 jmp .skipelse0
.skip3then
.skipL048
	LDA temp1
	CMP #3
     BCS .skipelse
.condpart5
	LDA screenindex
	CLC
	ADC #4
	STA screenindex
	LDA #8
	STA ypos
 jsr .setlevel
 jmp .doneherowalk

.skipelse
.skipelse0
.L049 ;  if joy0up then if ypos  >  8 then ypos = ypos - 1 : herodir = 2 : goto doneherowalk else if temp1  >  0 then screenindex  =  screenindex  -  4 :  ypos  =  172 :  gosub setlevel :  goto doneherowalk

 lda #$10
 bit SWCHA
	BNE .skipL049
.condpart6
	LDA #8
	CMP ypos
     BCS .skip6then
.condpart7
	DEC ypos
	LDA #2
	STA herodir
 jmp .doneherowalk
 jmp .skipelse1
.skip6then
.skipL049
	LDA #0
	CMP temp1
     BCS .skipelse
.condpart8
	LDA screenindex
	SEC
	SBC #4
	STA screenindex
	LDA #172
	STA ypos
 jsr .setlevel
 jmp .doneherowalk

.skipelse
.skipelse1
.
 ; 

.L050 ;  if joy0left then if xpos  >  8 then xpos = xpos - 1 : herodir = 1 : goto doneherowalk else if temp2  >  0 then screenindex  =  screenindex  -  1 :  xpos  =  152 :  gosub setlevel :  goto doneherowalk

 bit SWCHA
	BVS .skipL050
.condpart9
	LDA #8
	CMP xpos
     BCS .skip9then
.condpart10
	DEC xpos
	LDA #1
	STA herodir
 jmp .doneherowalk
 jmp .skipelse2
.skip9then
.skipL050
	LDA #0
	CMP temp2
     BCS .skipelse
.condpart11
	DEC screenindex
	LDA #152
	STA xpos
 jsr .setlevel
 jmp .doneherowalk

.skipelse
.skipelse2
.L051 ;  if joy0right then if xpos  <  152 then xpos = xpos + 1 : herodir = 3 : goto doneherowalk else if temp2  <  3 then screenindex  =  screenindex  +  1 :  xpos  =  8 :  gosub setlevel :  goto doneherowalk

 bit SWCHA
	BMI .skipL051
.condpart12
	LDA xpos
	CMP #152
     BCS .skip12then
.condpart13
	INC xpos
	LDA #3
	STA herodir
 jmp .doneherowalk
 jmp .skipelse3
.skip12then
.skipL051
	LDA temp2
	CMP #3
     BCS .skipelse
.condpart14
	INC screenindex
	LDA #8
	STA xpos
 jsr .setlevel
 jmp .doneherowalk

.skipelse
.skipelse3
.
 ; 
Link to comment
Share on other sites

Its the "if...then" clauses after the else statements that are throwing the parser. The same thing happens in bB. To be honest, I'll probably just document that limitation and move on, as it would require a lot of rewrite for this specific case, and I've never seen anybody report running into this in my years of hanging out in the bB forums.

 

In your provided code, the else doesn't serve a useful purpose. The goto at the end if the if...then statement before the else statement ensures that any lines following are all "else". So remove the else and stick the stuff after it on the following line.

Link to comment
Share on other sites

In your provided code, the else doesn't serve a useful purpose. The goto at the end if the if...then statement before the else statement ensures that any lines following are all "else". So remove the else and stick the stuff after it on the following line.

Well, it will need to be restructured a bit as the else still assumes joy0down, so either I need to have another line redundantly checking for that, or I need to reverse the logic and add gotos so I can spread out the code in a way that will compile.

 

EDIT: Got it working fine. :) Thanks.

 

I've gotta say, I love the idea of the savescreen, restorescreen, and plotting. Adding pixels to a bitmap seems like it would allow one to do a lot of cool effects that may otherwise be difficult in purely tile-based systems.

 

Is there a quick way to print characters to the screen that doesn't depend on specified alphadata? For instance, to create the effect of scrolling characters on-screen.

 

Although maybe one could define the full phrase in alphadata, then keep shifting over one tile while writing the alpha data one character at a time?

Edited by Cybearg
Link to comment
Share on other sites

I also have some more questions.

 

1. I've been adding some logic that checks to see what adjacent rooms are and block off/leave open walls depending on whether those rooms can be entered or not. I do this using plotchars like so:

   temp3 = screenindex - 4: temp3 = _Screen_Table[temp3]: temp4 = screenindex / 4
   if temp4 = 0 || temp3 = 255 then plotchars 'fgfgfgfg' 1 48 0

However, the plotted tiles don't look quite like the others. While the already-set tiles have black backgrounds, the plotted tiles don't. Why is that?

post-34988-0-46466400-1412959009_thumb.png

 

2. I've gotten a bit used to IntyBasic, so I'm having to re-learn the simpler logic necessary for the batariBasic compiler. Why does the batariBasic compiler (and, by extension, 7800basic) seem "weaker" in the sense that it can't figure out any logic in if statements and allows only a few statements to follow an if, while in IntyBasic, I can write lines like this:

IF CONT1.BUTTON AND pres_y = 0 AND pres_cool = 0 THEN #temp1 = ((Y1 + 1) /8) * 4: #temp1 = peek( $200 + #temp1 + (#temp1 * 4) + ((X1-9)/8)): IF (#temp1 AND 7) <> 3 AND (#temp1 AND 7) <> 2  THEN pres_x = x1: pres_y = y1: pres_col = (RAND AND 3): pres_state = (RAND AND 7): IF (flags AND 2) = 0 THEN flags = (flags XOR 2)

or this:

IF e_type(temp1) = 4 THEN e_x(temp1) = e_x(temp1) - 1: temp2 = e_y(temp1): IF temp2 < 0 OR temp2 > 96 THEN e_type(temp1) = 0 ELSE IF y1 < 48 THEN e_y(temp1) = temp2 - 1 ELSE e_y(temp1) = temp2 + 1 

... And it compiles just fine?

 

3. How many cycles does a plotchars routine take (or rather, how many times can they be called between drawscreens) and is there an efficient way to plot a column of tiles without requiring a new plotchar for each row?

 

4. Why are plotchars based on the on-screen pixel coordinates for x, but based on the tile's index for y? Why can't y also be per-pixel?

Edited by Cybearg
Link to comment
Share on other sites

However, the plotted tiles don't look quite like the others. While the already-set tiles have black backgrounds, the plotted tiles don't. Why is that?

MARIA provides transparency + 3 colors. If you look at the source image, it's using 4 colors. That black subs in for transparency if you do that.

 

You're plotting your tiles on top of existing sandy tiles, and the tiles beneath are showing through the transparent color.

 

If you want more WYSIWYG, then just design tile images with 3 colors + transparency.

 

 

2. I've gotten a bit used to IntyBasic, so I'm having to re-learn the simpler logic necessary for the batariBasic compiler. Why does the batariBasic compiler (and, by extension, 7800basic) seem "weaker" in the sense that it can't figure out any logic in if statements and allows only a few statements to follow an if, while in IntyBasic, I can write lines like this [...]

I can go into the specific technical reasons why the parser design doesn't like doing this right now, but I take you want the philosophical reason...

 

Splitting up the logic on different lines doesn't use any more or less space. Personally, I find what you wrote less readable than the multi-line equivalent which produces the same assembly code, and which can have multiple levels of indenting to quickly infer where the code blocks start and end.

 

In an ideal world you'd have the choice to concatenate if...thens all you like, but in my ideal world I won't spend the next dozen weekends rewriting and debugging new bugs introduced by a rewrite. :)

 

 

3. How many cycles does a plotchars routine take (or rather, how many times can they be called between drawscreens) and is there an efficient way to plot a column of tiles without requiring a new plotchar for each row?

The number of cycles is variable due to some optimizations I run when the 7800basic code uses constant values in some positions. As to "how many" between drawscreens, less than 24. That's the currect sprite-limit I tested for, and the plotchar routines are a bit more complicated than sprite plotting commands.

 

If you need to do a bunch of plotchars between frames, instead of during screen or level loading routines where you can spread them out over a frame or two, then you're probably doing it wrong. Instead plot everything up front and "savescreen" it. If it needs to change then plot a RAM memory location rather than ROM and change the RAM values rather than replotting it.

 

More to that, MARIA doesn't have enough rendering time to plot multiple layers of characters. If you write a game like this it will likely not work on real hardware. The upcoming MESS 7800 release will address this and you'll see it in emulation there.

 

 

Why are plotchars based on the on-screen pixel coordinates for x, but based on the tile's index for y? Why can't y also be per-pixel?

MARIA can't draw characters straddling zones, so they need to go at a Y location of 0, zoneheight, zoneheight*2, zoneheight*3, etc. I used character positions for Y in the the character plotting routines, and documented it as such, so people wouldn't get the mistaken idea that characters could be plotted between zones.

Link to comment
Share on other sites

I've got a question about comparing using score0 as a 'points' counter and using IF...THEN statements. I have set up a shop where the player buys something and if he has enough points, the item should disappear, score0 should reduce by 25 and score1 should increase.

 

So I reduced score0 to four digits meaning that if something is 100, it's actually 1. So what about using two digit numbers like 25? For instance...

if score0=0025 && boxcollision(xpos,ypos,16,16,orange_x,orange_y,16,16) then orange_x=200:orange_y=200:  score0=score0-0025: score1=score1+0015:store_response=1

So far, the code above doesn't work. I'm wondering if I need to use BCD for something like this?

Link to comment
Share on other sites

Alright this may be a dumb question, but I've finally got around to sitting down with this, currently just going through the sample code, one thing I don't understand is why in the adventure demo, and multisprite demo is tileset_blanks loaded and referred to as the tilemap rather than tileset_rocks? What role does tileset_blanks play?

 

 

**edit**

 

Also where can I find a list of 7800 colors and their hex values? I notice them being assigned to palettes.

Edited by Loktar
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...