Jump to content
IGNORED

Sprites in Pascal


Rossman

Recommended Posts

I'm programming a little card game in Pascal. Card games in Pascal are fun programming exercises but are generally not visually interesting. I'm looking at changing up the UI to show cards being dealt, presented, flipped. I looked into turtlegraphics, but I'm thinking I'll use sprites. If for no other reason than because I have access to far more documentation on sprites than turtlegraphics.

 

Sprites will make it a non-portable Pascal program, but my goal in doing this wasn't to create a portable Pascal program. The current text UI version of the code is portable to any UCSD system, save for the random number generator. I found some pseudo-random-number generators that I believe could be easily implemented, so that's likely a problem easily solve-able. I'm just going for a little visual improvement.

 

With all of that as a preamble, here's my question.

 

It's been a long time since I coded sprites, so I'm creating some test cases to model out the things I'll need to do for this to work. My test moves a double-sized sprite (not magnified) from (y=95,x=128) at velocity (y=-8, x=0) for a countdown of 64, then a velocity of (y=-4,x=0) for a countdown of 64. Then it holds position. I'm just experimenting with showing a card come out fast, then slow down, then stop. (Inspired by the truly beautiful motion of the Q*Bert demo we saw at the Evanston meet-up a few weeks ago!) I'd expect the sprite to end up at 95 + ((-8/32*64) + (-4/32*64)), or y=71, x=128. It isn't. It's ending up at double the distance: y=47, x=128.

 

The velocity/32 every 60th of a second math comes from page 145 of the Pascal Compiler manual. This suggests a sprite travels just about 2 pixels every 60th of a second. Or, assuming it's an octal and not a decimal clock, 1 / 32 * 64 == 2.

 

The Editor/Assembler manual has a different description on sprite motion. Page 341 says, that a velocity of >01 causes a sprite to move one pixel every 16/60th. Assuming that the velocity is the same, it seems to me that the assembler manual says we get 4 pixel moves every (roughly) 60th of a second. Back to our octal clock, 16/64 == 4.

 

If I'm reading this right, the sprite speed in the assembler manual would describe the 2x difference in velocity that I'm seeing with where my sprite is ending up.

 

I haven't run all possible tests yet to figure out if I'm just thick-headed. I haven't coded sprites in assembly or XB routines to time comparisons, I haven't re-timed using a standard sized sprite, etc.

 

But anyone who is familiar with sprite motion who knows the velocity I should expect, I would appreciate it.

 

Best regards,

 

 

R.

 

  • Like 1
Link to comment
Share on other sites

Unfortunately I don't remember the velocity you should expect in Pascal. I've not used sprites much in Pascal on the 99/4A, but mainly written software that are either text only or system oriented.

However, after deep dives into the p-system implementation on the TI 99/4A, I can at least say for sure that the sprite automotion logic described in the Editor/Assembler manual is the one that's built-in into the console ROM in the computer. That code presumes a certain VDP RAM table setup, or it will modify the wrong items in VDP memory.

But the p-system has a different memory setup, so it also has its own sprite movement code. Thus it's not unexpected at all if you get contradictionary information about sprite speeds when comparing the E/A manual with the Pascal compiler manual's descripion of the sprite handling unit.

 

I can also add that the p-system's automatic motion routines handle the sprites in two blocks. If you use automatic motion, you get better performance if all sprites using it are in group 0-15 or group 16-31. The logic will skip half of the motion code if only one of these groups contain moving sprites. Even if you only use two sprites with motion, you'll suffer a speed penalty if they belong to different groups.

Edited by apersson850
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Wow, I didnt realize the p-system had its own sprite movement. Thats good to know. I probably need to spend a little more time just becoming familiar with the p-system. Im making assumptions about what it does and does not do.

 

Great tip on the movement and blocks. Thank you.

 

Ive only been experimenting with sprites on Pascal for a few days now, but it strikes me a little bit like its not as complete an implementation as in other languages on the TI. So far, it does everything I need it to do (and Im really not doing all that much). Not that its incomplete, and maybe its just the implementation style, but my first impression is that sprite implementation under Pascal has fewer helper methods as in other languages.

  • Like 1
Link to comment
Share on other sites

There are Sprites in the air it seems. I stayed up late last night trying to grok the ISR code for Sprite motion.

 

My homemade sprite auto-motion system is wrong, but I don't understand exactly how. I was taking a velocity(x,y) (+ or - ) from an array and simply adding it to the current sprite position.

This gave very ragged motion. I checked XB and the sprites are only moved 1 pixel at a time from what I can see regardless of the velocity used. (Classic99 debugger)

 

So if anybody can explain what's going on with this ROM code I would be all ears.

Like what is this auxiliary data in the motion table?

 

This is from TI Intern, but I have added some my own comments to help me stay on track

093A C002 MOV   2,0
093C C0A2 MOV   @>0002(2),2             Fetch INT address
093E 0002
0940 0692 BL    *2                      And execute
0942 C090 MOV   *0,2                    Next Int routine
0944 10F9 JMP   >0938
0946 0460 B     @>0AB8                  End interrupt from CRU
0948 0AB8
094A 1D02 SBO   >0002                   Clear VDP interrupt
094C D060 MOVB  @>83C2,1                 Fetch interrupt flag byte
094E 83C2
0950 0A11 SLA   1,1                      No interrupt permitted
0952 1702 JNC   >0958
0954 0460 B     @>0A84                   then jump
0956 0A84
0958 0A11 SLA   1,1
095A 1846 JOC   >09E8                    No sprite move permitted, then jump
095C D320 MOVB  @>837A,12                Number sprites  -> R12
095E 837A
0960 1343 JEQ   >09E8                    IF sprite_cnt=0 then skip all this

0962 098C SRL   12,8                     Move sprite count to other byte
0964 0202 LI    2,>8800                  VDP RD -> R2
0966 8800
0968 0203 LI    3,>8C00                  VDP WD -> R3

096A 8C00
096C 0208 LI    8,>0780                  Sprite motion table -> R8
096E 0780

* begin loop for each sprite
0970 D7E0 MOVB @>83F1,*15               Write address motion table
0972 83F1
0974 D7C8 MOVB  8,*15                   load Spr motion table to VDP Write address
*=====================================================
0976 04C4 CLR   4
0978 D112 MOVB *2,4                     read Motion table Y velocity ->R4
097A 04C6 CLR  6
097C D192 MOVB *2,6                     read Motion table X X velocity->R6
097E 0844 SRA  4,4                      ?? shift   Y velocity right 4
0980 D152 MOVB *2,5                     read Y Auxiliary sprite data->R5
0982 0845 SRA  5,4                      shift Aux data 4 right (16/)
0984 A144 A    4,5                      ADD R4->R5
*====================================================
0986 D1D2 MOVB *2,7                     Read Y Auxiliary sprite data-> R7
0988 0846 SRA  6,4                      Shift X motion by 4 right
098A 0847 SRA  7,4                      Shift X aux data 4 right
098C A1C6 A    6,7                      Add R6->R7
098E 0228 AI   8,>FB80                  Address sprite descriptor table
0990 FB80
0992 D7E0 MOVB @>83F1,*15               Write address
0994 83F1
0996 D7C8 MOVB 8,*15
====================================================
0998 04C4 CLR  4
099A D112 MOVB *2,4                     Fetch Y position->R4
099C A105 A    5,4                      add Y motion+aux+Ypos->R4
099E 0284 CI   4,>C0FF                  are we at X,Y limits (192,256)
09A0 C0FF
09A2 1209 JLE  >09B6                    if not off screen, goto 9B6
09A4 0284 CI   4,>E000                  are we at start of screen ( 224,00)
09A6 E000
09A8 1B06 JH   >09B6                    if HI goto 9b6
09AA C145 MOV  5,5                      else test for 0
09AC 1502 JGT  >09B2                    if R5>0 goto 09B2
09AE 0224 AI   4,>C000                 ?? what is this for
09B0 C000
09B2 0224 AI   4,>2000                 ?? and why add this
09B4 2000
09B6 04C6 CLR  6                       *screen limit tests
09B8 D192 MOVB *2,6
09BA A187 A 7,6
09BC 0268 ORI 8,>4000                   setup VDP address for writing
09BE 4000 
09C0 D7E0 MOVB @>83F1,*15
09C2 83F1 
09C4 D7C8 MOVB 8,*15 
09C6 D4C4 MOVB 4,*3                     Write X,Y positions to Spr. desc. table
09C8 0228 AI 8,>0482                    ?
09CA 0482 
09CC D4C6 MOVB 6,*3
09CE 06C5 SWPB 5 
09D0 D7E0 MOVB @>83F1,*15               Write address motion table
09D2 83F1 
09D4 D7C8 MOVB 8,*15 
09D6 0945 SRL 5,4 
09D8 D4C5 MOVB 5,*3                     Write auxiliary values
09DA 06C7 SWPB 7 
09DC 0947 SRL 7,4 
09DE D4C7 MOVB 7,*3 
09E0 0228 AI 8,>C002                    New address motion table
09E2 C002 
09E4 060C DEC 12                        Last sprite?
09E6 15C4 JGT >0970                     No, once again
* end sprite loop

09E8 0A11 SLA 1,1                      START of sound processing...

Link to comment
Share on other sites

I've done a lot of sprite work in assembly language. I never use auto-motion; using VDP RAM at a specific place in memory for what is essentially just a velocity table array is silly when you can just store that (and more besides) in CPU RAM.

 

All sprites move one pixel at a time, the difference is how many "ticks" of the VDP interrupt pass between movements. Your best bet is to just play with the numbers until you get the effect you want, I've done this myself with character movement and shot speed for my Gauntlet clone.

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

 

There are Sprites in the air it seems. I stayed up late last night trying to grok the ISR code for Sprite motion.

...

 

Compare the TI Intern listing with ROM-4A.lst in the ZIP file at the end of post #1 of The TI-99/4A Operating System. It contains the comments of the original TI authors and might offer more insight.

 

...lee

  • Thanks 1
Link to comment
Share on other sites

Amusing moment for the day when the sprites developed a mind of their own. This is not a complaint or a criticism or a confusion, just one of those moments when I realize I have to be precise in communicating my intentions via code.

 

I'm working in a test bed just to familiarize myself with sprites in Pascal and how it will relate to my code. I mocked up a 10 of Hearts in a multiplied sprite, visually represented as a number (10) over the suite (heart), bitmapped. It looks crisp. If you're playing the game, you know you were dealt a 10 of Hearts, and not an eleventy-leven of blobulas.

 

I setup a second card deal in my test code, which was largely a duplication of my first set of code (yes, shame!), to figure out multiple sprite motion. I am using two sprites for every card. Because the background color is always transparent on a sprite in Pascal, if you want some color for the background other than the screen background color, you need some other color wherever the sprite lands. So for convenience, I'm using a second sprite mapped to the x,y coordinate of where the first sprite will land. All I do is have a second sprite (of lower priority) waiting in that location with its color set to transparent, and at the moment the first sprite lands the second sprite changes is color from transparent to the color that I want (in this case, color 15). So I have a green background screen color, a white card background and a red or black foreground.

 

It worked very nicely on the first sprite pair. And it worked on the second sprite pair. But then, after a second, the second sprite pair just ... took off, at different velocities, on the x axis.

 

I don't know why the second pair choose to go in motion. Maybe they need to sow their wild oats. But I can't hang this on the p-code card. It was sloppy programming on my part. I had a countdown := 64 in the final packet declaration of both the first and second sprite pair. It didn't affect the first pair, but it did the second. I set the countdown to 0 on the foreground and background sprites, and they are staying put. I don't know why only the second pair was affected, but I'm not as concerned about it. I learned today via the early post from apersson850 that the p-code card has its own sprite movement code. And I am reminded that I need to be explicit in my intent.

 

But it was kinda funny to see two sprites act on their own, goin' up the country.

  • Like 1
  • Haha 1
Link to comment
Share on other sites

I'd say that the p-system rather has more sprite support than most other languages/operating systems for the TI. It's the only system I know of that implements the ability to declare linked lists of sprite appearance, position and motion, and which will then execute these lists in parallel to what your program is doing.

  • Thanks 1
Link to comment
Share on other sites

I worked on the card flip logic tonight. Card is dealt via sprite from a (currently mythical) shoe, it arrives to the player, and it flips over to reveal it is a Hook of Clubs or a duck of Diamonds. I can integrate the card flip motion with a timed sprite motion so it looks like a fluid action. Just need to define a lot of runway for the motion.

 

I've been working in the Classic99 emulator. Maximum props to @Tursi for this. I have no complaints or criticisms. I've learned more, faster, because of the emulator. I know what it was like to learn this 30 odd years ago. Humans were faster than the hardware when it came to asking, "why".

 

I have noticed a few bad behaviors. I don't know if these are unique to the emulator or if I would encounter these on real iron with the p-code. This is just observation.

 

One is that when I have a compile error in my sprite code - some overlooked typo on my part but always in a code block of...

 

(linkname)^.packet : [something .. something]

with (linkname)^ do begin

.. code ..

end;

 

What I've found is that I'm more prone to ***STACK OVERFLOW*** errors. I didn't get any with my bog standard Pascal for developing the card logic. But I seem to be running into these more frequently when working with Sprites under Pascal. It's a minor inconvenience, but it is a definite change in the behavior of the compiler.

 

Maybe it's just the compiler's way of telling me my code sucks big time. Maybe standard UCSD Pascal wouldn't do that, but some clever TI engineer figured out - temporally shifted, of course - it was time to let me know.

 

Another is - and I don't have a great accounting for this - that sometimes in the Pascal editor in D)elete mode, the cursor occasionally moves backwards rather than forward when I delete using the spacebar! Easily adjusted to, sometimes only requires an exit - re-entry of the editor and never more than a warm reboot.

 

Neither are complaints, just observations. It goes back to @apersson850 telling me that that the p-code is a world unto itself.

 

But a craftsman never blames his tools. Both are reflections on my cave-man coding.

  • Like 1
Link to comment
Share on other sites

Sadly I can't offer much insight as to whether p-Code card behaviors are emulation artifacts or not - I never used one in real life. This is one of the rare cases where I'd say compare to other emulators and see if they do it too - at least if it's easy to reproduce. Since Pascal is pure software I'd /expect/ the behavior to be correct, but it's amazing what subtle bugs one never considered can do. ;)

  • Like 1
Link to comment
Share on other sites

@Tursi, if I was a little more careful in my code I wouldn't have the exceptions in the first place. The offending s/w in question is entirely mine.

 

It's naive on my part, but I never really thought much about how the p-code card is a world until @apersson850's post. I assumed more pass-through, but in making that assumption I'm not showing enough respect for it.

 

And none of this is an obstacle to my little stick man card game.

  • Like 1
Link to comment
Share on other sites

Wait a minute!!! Gauntlet clone? I'm not going to let that slide.

 

 

All sprites move one pixel at a time, the difference is how many "ticks" of the VDP interrupt pass between movements. Your best bet is to just play with the numbers until you get the effect you want, I've done this myself with character movement and shot speed for my Gauntlet clone.

Edited by unhuman
  • Like 2
Link to comment
Share on other sites

I've done a lot of sprite work in assembly language. I never use auto-motion; using VDP RAM at a specific place in memory for what is essentially just a velocity table array is silly when you can just store that (and more besides) in CPU RAM.

 

All sprites move one pixel at a time, the difference is how many "ticks" of the VDP interrupt pass between movements. Your best bet is to just play with the numbers until you get the effect you want, I've done this myself with character movement and shot speed for my Gauntlet clone.

 

Thanks for this. Apologies for not replying faster.

 

You are correct, I could play with the numbers to get what I want.

I have been using a multi-tasking system for my Forth system and I re-created the ISR in a little task. Forth handles all this stuff like Assembler so I too use CPU RAM for my position and motion tables and the write the block of sprite positions to VDP RAM all at once. So I am on the right track there at least. :-)

 

I have been reviewing the ISR code and looking at the debugger to see how it works. Here is what I think I see:

 

1. The Motion values are signed bytes. Positive: 0 to 7F, Negative: 80 to FF

2. The Aux values, beside each motion pair, are incremented every tick of the interrupt, by the amount in the motion value

These are the little counters that run for each sprite's X and Y value.

3. When the AUX value rolls over the sprite is moved 1 pixel.

So bigger values of Motion cause the AUX register to rollover faster of course therefore those sprite moves faster.

 

So at this stage I am debating how best to handle this in Forth just to see what I can do without writing an ASM version.

Forth deals with ints better than bytes so I am thinking that I will use 16 bit tables for motion and timers (AUX) and just shift the motion values to upper byte.

Since everything is multiplied by >100 it will all count the same speed as using bytes, but my Forth operators will be faster for 16 bit values.

 

That's all I have at this time.

 

Thanks again for confirming my thoughts on the matter.

Edited by TheBF
  • Like 1
Link to comment
Share on other sites

@Rossman: MAME also has a complete P-system support, so you could give it a try, just for comparing. I can give you a quick summary how to get it going.

 

Thank you @mizapf, at some point I will want to set up MAME just to do it. Classic99 is doing the job for me. Zero stack overflow errors tonight directly correlates to zero unforced errors in my code. For now, I want to concentrate on code, not infrastructure. I do not have an infrastructure problem.

 

I did a simple lane change mock-up to show cards coming from the same shoe. It's just a math problem, but I did it because I wanted the satisfaction of doing it. #octalmath #base8rules #decimaldinosaurs

 

I really need to be working on implementing the stick-man sprite visualizations with the game logic. What I've learned from the visualization tests is that it will significantly increase the size of the code base. Which tells me I really should refactor the game logic first, as it is plagued by the characteristics of someone re-learning structured programming.

 

Thanks to everyone for your insights on sprites, they have changed the trajectory of how I see my path of re-learning Pascal progressing.

 

  • Like 1
Link to comment
Share on other sites

 

Thank you @mizapf, at some point I will want to set up MAME just to do it. Classic99 is doing the job for me. Zero stack overflow errors tonight directly correlates to zero unforced errors in my code. For now, I want to concentrate on code, not infrastructure. I do not have an infrastructure problem.

 

As you like. I did not want you to change the infrastructure, but just have a means of ruling out such issues. I think many of you still overestimate the efforts of installing MAME; it usually sounds like "when I have some free days" or "going to retire next year, mayby then". ;)

  • Like 1
  • Haha 1
Link to comment
Share on other sites

@mizapf I think I'll take you up on that MAME offer sooner rather than later.

 

I went to add just one more sprite tonight to my test code, for the shoe. I was hoping to have a sprite in the foreground from which I could dispense a card (the background transparency should have made it look pretty good), move it due west (out of the shoe), then due north (direction of my test deal). This is the same routine as I've been through every night for the past week: these are just math problems. Only, the code won't compile. There's nothing obviously wrong, but I am encountering a **STACK OVERFLOW** mid-procedure that will otherwise compile when my additional code - mostly a few type declarations and what not - are commented out.

 

This isn't an accusation about the emulator, but I need a comparison environment to isolate the source of the problem. It is entirely possible that the source of the problem is something I coded that is setting this off. Incremental code additions have yielded a STACK OVERFLOW mid-compile, for no obvious reason.

 

But "no obvious reason" starts with me. I may have an errant instruction that is obvious to the compiler even if it isn't obvious to me. Also, this is test code that has grown recklessly. I may need to tighten the code so I can test the situation better.

 

Still, having a MAME enviro might be an advantage.

Link to comment
Share on other sites

I believe I have found the source of the problem. It has to do with how I structured my code.

 

I couldn't understand why I was experiencing **STACK OVERFLOW** errors during compile. If I got this during runtime, I'd get it. But stack overflow during compile? Seems odd.

 

This sprite code is experimentation code for me. I am modeling things that I want to try. This isn't production code, and I am not going to harvest anything but experience from this. So it isn't clean, it isn't modular, it isn't efficient. For example, I have a lot of separate string variables when I should have arrays. So in that regard, it's a bit caveman.

 

Refactoring it was never important because I've just about completed my experiments. Only, as I neared my destination, I kept running into stack overflow.

 

All of my sprites are double-sized, meaning they use 4 different character definitions. For reasons of convenience, I was declaring my sprite patterns as constants. For example:

 

const

crda = '3F20242C24242E20';
crdb = '202227272321203F';
crdc = 'F80848A8A8A84808';
crdd = '0888C8C8880808F8';
Yes, this should be an array, probably a packed array. But in my test code I only have one card face defined (just the 10 of hearts, not all 52 cards, this is experimental code!). But in addition to the card, I do have a background pattern, and a card flip sequence, and some other things. In all, I had 19 x 8 character string sprites defined.

 

And that, I think, was what was causing all my stack overflow errors.

 

So with that as my hypothesis, I eliminated the constant definition and moved the character pattern to the SET_PATTERN statement. No stack overflow errors at odd times during compile. It works just fine.

 

It seems there is something I have to learn about limitations on constants. Maybe a memory limitation on how constants are treated. I wasn't creating highly modularized code here either, so maybe more to the point, I need to do better at creating modular code that is memory efficient.

 

Well, and obviously, the converse is true: there is never an excuse for writing sloppy code. Which I did. My experiments need more discipline.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

I've never suffered stack overflow during compile time on my 99/4A. But I do know that the handling of string constants is a bit peculiar in UCSD Pascal, so maybe you've run into something the system can't handle, even if it actually ought to.

 

I'm really glad you posted this. When I started coding my little game a few weeks ago, I expected that I would have to implement strings as packed arrays of type char. In reading my (old) 70's era Pascal academic books, it seems like string type was not indigenous to Pascal. I was pleasantly surprised to see that there is a native string type in UCSD on the TI. Do I understand this correctly? Were strings a later addition to Pascal?

Link to comment
Share on other sites

The data type string, which is different from a packed array of char in that the string also has a current length, is one of the extensions to standard Pascal (whatever that is) that you'll find in UCSD Pascal.

The same design was used by Turbo Pascal 4.0, for example. That Turbo Pascal edition borrowed quite a lot from UCSD Pascal. Like the unit concept, too.

 

I noticed another compiler thing you wrote about. If we assume this

var
  a: array[0..3] of integer;
  b: array[0..3] of integer;

Then they are frequently not considered to be of the same basic type.

But if you do like this

type
  arraytype = array[0..3] of integer;
 
var
  a, b: arraytype;

Then they are considered the same by the compiler.

This works too

var
  a: arraytype;
  b: arraytype;

The type declaration makes the compiler consider them to be of the same basic type.

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