Jump to content
IGNORED

Shadow Of The Beast for the TI-99/4A


The Codex

Recommended Posts

I wonder what could be done by just storing 8 versions of the background so you can scroll smooth? I think you would need a background with a repetitive nature too.

 

I've been messing around with this concept.

 

I did the math out of curiousity to see what it would take to do a fast smooth scroll. There's enough VRAM in Graphics0 for 4 copies of the full character set. If you wanted, then, you could have 4 different shift positions for 128 characters (since a single character needs two slots when it's shifted). Then you can "scroll" the screen by 2 pixels at a time just by changing the base address of the pattern table. I'm successfully using this technique in my Coleco Super Space Acer game right now to move the bosses smoothly horizontally (next release will show that).

 

If you wanted single pixel accuracy, you could divide the character set in half, and have each table define "two" sets of 128 characters (in theory only, not in hardware). Scrolling would then entail changing the pattern table base and redrawing the screen (768 bytes). A little more expensive, but still feasible in vblank. However, with the pre-shifting, you'll only have 64 characters to work with.

 

There are a couple of limitations with this. The first is that if you have two characters touching each other, you always need them touching each other through the scroll, so each combination would need to be accounted for. The second is that you're somewhat out of luck when it comes to color sets - first because they'll be artificially smaller due to the character overlap, second because you can't put two different colors side-by-side (you'll need at least one character of background color between them), otherwise colors will distort as they "scroll".

Link to comment
Share on other sites

To put a little more detail... in SSA, the bosses get characters 121-255. Because they are a block of graphics in a fixed layout, I only actually need one extra character per horizontal row to do the smooth scrolling - the largest boss is 12 rows by 10 columns, which is 120 characters. Since there are 12 rows, I need 12 extra characters for it to shift into, so it uses a total of 132 characters.

 

My game defines pattern tables at >1000, >1800, >2000, and >2800. The offset of >0800 is chosen because this is the minimum step in the VDP.

 

When the boss needs to come in, I load the default patterns into the first table. I then have a small piece of code that preshifts by 2 pixels into each of the other character sets. (The patterns from 0-120 are static in all four tables, so they don't appear to change). It causes a noticable hiccup in the game, but I will smooth this out by doing it over a number of frames.

 

Ultimately, when moving the boss across the screen, I track it's column by pixel value divided by 2 (so, from 0-127). We need to get two values from that: the pattern table to select, and the horizontal column to draw at. The pattern table is easy, we just take the two lower bits with an AND. The value to write to the VDP register is the base value (>1000 = >0800*2, so '2' is the base) + that, so : (0x02 + (bc&0x03)). That gives the correct sub-character offset. The actual character is easy, too... it's the column divided by 4, which is just a shift operation (bc>>2). Draw the boss normally at that column, and it's done.

 

With the calculations done that way at draw time, I never need to consider the scroll mechanism when moving the boss, I just update the 'bc' (boss column) as I choose.

 

The other nice thing... even with all that, there's still plenty of room for a full sprite pattern table in VRAM, too. So this doesn't limit anything, really, it just uses a little more of the often-unused VRAM space. My VRAM map looks like this:

 

// VRAM map:

// >0000 Screen Image Table

// >0300 Sprite Descriptor Table

// >0380 Color Table

// >03A0 (Unused)

// >0800 Sprite Pattern Table

// >1000 Pattern table (scroll 0)

// >1800 Pattern table (scroll 2)

// >2000 Pattern table (scroll 4)

// >2800 Pattern table (scroll 6)

// >3000 (Unused)

 

There's still a bit over 5k left there. Unfortunately each pattern table takes 2k, so you can't do 8 full tables, it would use all 16k of VRAM (unless you were willing to sacrifice some characters for the other tables, of course! Might not be a bad alternative to my half-table suggestion above.) Scrolling by 2 pixels only needs 4 tables, which is just 8k.

Link to comment
Share on other sites

To put a little more detail... in SSA, the bosses get characters 121-255. Because they are a block of graphics in a fixed layout, I only actually need one extra character per horizontal row to do the smooth scrolling - the largest boss is 12 rows by 10 columns, which is 120 characters. Since there are 12 rows, I need 12 extra characters for it to shift into, so it uses a total of 132 characters.

 

My game defines pattern tables at >1000, >1800, >2000, and >2800. The offset of >0800 is chosen because this is the minimum step in the VDP.

 

When the boss needs to come in, I load the default patterns into the first table. I then have a small piece of code that preshifts by 2 pixels into each of the other character sets. (The patterns from 0-120 are static in all four tables, so they don't appear to change). It causes a noticable hiccup in the game, but I will smooth this out by doing it over a number of frames.

 

Ultimately, when moving the boss across the screen, I track it's column by pixel value divided by 2 (so, from 0-127). We need to get two values from that: the pattern table to select, and the horizontal column to draw at. The pattern table is easy, we just take the two lower bits with an AND. The value to write to the VDP register is the base value (>1000 = >0800*2, so '2' is the base) + that, so : (0x02 + (bc&0x03)). That gives the correct sub-character offset. The actual character is easy, too... it's the column divided by 4, which is just a shift operation (bc>>2). Draw the boss normally at that column, and it's done.

 

With the calculations done that way at draw time, I never need to consider the scroll mechanism when moving the boss, I just update the 'bc' (boss column) as I choose.

 

The other nice thing... even with all that, there's still plenty of room for a full sprite pattern table in VRAM, too. So this doesn't limit anything, really, it just uses a little more of the often-unused VRAM space. My VRAM map looks like this:

 

// VRAM map:

// >0000 Screen Image Table

// >0300 Sprite Descriptor Table

// >0380 Color Table

// >03A0 (Unused)

// >0800 Sprite Pattern Table

// >1000 Pattern table (scroll 0)

// >1800 Pattern table (scroll 2)

// >2000 Pattern table (scroll 4)

// >2800 Pattern table (scroll 6)

// >3000 (Unused)

 

There's still a bit over 5k left there. Unfortunately each pattern table takes 2k, so you can't do 8 full tables, it would use all 16k of VRAM (unless you were willing to sacrifice some characters for the other tables, of course! Might not be a bad alternative to my half-table suggestion above.) Scrolling by 2 pixels only needs 4 tables, which is just 8k.

 

K, short on space, what's the cycles situation? With TI graphics being highly compressible, even simple compression like RLE would make a lot of room. If cycles are tight, since you're just short of fitting, compress/process just enough, x line/characters needed to fit everything without sacrificing characters.

Edited by in8regs
Link to comment
Share on other sites

TI engineers never got as far as we have lately. Even TI's best scrollers weren't as tight as 2 pixels from what I understand. Great stuff man.

Parsec is still somewhat technically unsurpassed. Scroll, speech, scratchpad, pixel perfect terrain collision detection while trying to maintain gameplay, that’s something ! The prototype Vanguard had very nice full screen scrolling using some of the VDP “tricks”.

 

http://www.youtube.com/watch?v=XHOnuOh4ahk

 

:)

Link to comment
Share on other sites

K, short on space, what's the cycles situation? With TI graphics being highly compressible, even simple compression like RLE would make a lot of room. If cycles are tight, since you're just short of fitting, compress/process just enough, x line/characters needed to fit everything without sacrificing characters.

 

You're sort of crossing ideas here, though. For the scroll trick I am describing to work, you have to have everything predefined. Each table /must/ take 2k because that is the granularity of the VDP. Pre-defining it allows a very fast scroll - just a couple of cycles until you cross a character boundary and have to redraw it.

 

You could try compression, but even full uncompressed writes to VDP would be tough to redescribe the entire character set in a single frame, unfortunately. (2k of data won't fit in vblank, making the scroll visible). You could do it if you restricted the number of characters - but then you are in the same situation as I described above. ;)

 

Restricting the number of characters is not necessarily a killer, it just depends on your background detail. Also, you can do a combination of the two - you have enough characters to define one screen, and dynamically update the character set as you move through the world. You just have to leave space in your character sets for the other VDP tables.. the SIT is the largest at 768 bytes, so that's 96 characters, leaving 160 characters available for the pre-defined scroll. You'd likely want sprites... you could use those 96 characters from one of the other tables for a fully distinct sprite table. That's enough characters for 24 of the 32 sprites, if you don't mind dynamically loading the patterns (which can be more easily staged in vblank). You could likewise double-buffer the sprite patterns, or even more than that, since you'd probably have 6 of the tables with unused space. :)

Link to comment
Share on other sites

Parsec is still somewhat technically unsurpassed. Scroll, speech, scratchpad, pixel perfect terrain collision detection while trying to maintain gameplay, that’s something ! The prototype Vanguard had very nice full screen scrolling using some of the VDP “tricks”.

 

Moon Patrol offers similar smooth scrolling of its mountains with parallax to boot, but it doesn't get much attention. Of course, it doesn't offer the rest of that, which was indeed well done. I never saw Vanguard, I wouldn't mind seeing what tricks it used.

 

We (well, I) took Parsec's scroll routine apart on the Yahoo list and I think it could have been optimized some. It hasn't been beaten, I feel, more because nobody's done it than because it's the best that can be done. Parsec achieved some kind of legendary status for that little piece of smooth scroll code. ;)

 

I take apart the Parsec code here: http://tech.groups.yahoo.com/group/ti99-4a/message/62752

 

Since that's the point of interest, I'll copy and paste that one. The point was that someone mentioned that Parsec copied the scroll routine to scratchpad RAM every single frame.

 

Parsec does indeed write and execute a small piece of scratchpad RAM

every frame, from 0x83E0. However, there's a lot of code executing in

the scratchpad, which does not get written repeatedly. Here's a function

at >8354:

 

65D0 bl @>8354
8354 movb @>8800,@>833c(R3)
835A inct R3
835C jlt >8354
835E b *R11
8360 mov @>833c(R4),R1
8364 src R1,0
8366 movb R1,@>8c00
836A inct R4
836C jlt >8360
836E b *R11

>8360 contains another subroutine:

 

6714 bl @>8360
8360 mov @>833c(R4),R1
8364 src R1,0
8366 movb R1,@>8c00
836A inct R4
836C jlt >8360
836E b *R11

Parsec does not appear to write the scroll routine to scratchpad every

frame. I tested this by adding code that would flag all executed bytes

in the scratchpad RAM, then emit debug if they were written to. After

the game was up and scrolling, I breakpointed the emulator and cleared

those flags, then resumed. There was no debug emitted. When I started

the game, that's when I saw the messages about data at >83Ex being

executed and written.

 

The code that ends up there, and thus gets rewritten, looks like this at

game start:

 

7F34 bl @>83e0
83E0 movb @>9000,R10
83E4 jmp >83e6
83E6 jmp >83e8
83E8 jmp >83ea
83EA b *R11

 

Note those three JMPs are NOPs, so all this does is a delay. Since it's

reading from >9000, this is probably just a little routine to safely get

the speech status without touching the 8-bit bus. This is the only code

that is written to scratchpad every frame, the rest lives there

permanently. It's only updated when it thinks it might be speaking.

 

By breakpointing on that code, it looks like Parsec has a 5-frame scroll

sequence, and it writes and calls this function for every frame. The

five frames seem to be:

 

-Scroll first quarter of scenery

-Scroll second quarter of scenery

-Scroll third quarter of scenery

-Scroll fourth quarter of scenery

-Scroll stars and animate ship flame

Link to comment
Share on other sites

Moon Patrol offers similar smooth scrolling of its mountains with parallax to boot, but it doesn't get much attention. Of course, it doesn't offer the rest of that, which was indeed well done. I never saw Vanguard, I wouldn't mind seeing what tricks it used.

Yeah, I had just posted and walked away, when I also thought of Moon Patrol.

 

Unzip the attached file to your DSK1 directory of Classic99.

 

 

 

EA3, File name: DSK1.MAIN3O, Program name: TEST

 

Start by watching the VDP registers in Debugger ...

 

icon_shades.gif

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