Jump to content
IGNORED

4Anoid - Arkanoid conversion for the TI-99/4A


OLD CS1

Recommended Posts

 

BTW, you're only 4 hours away from me now... we ought to get together and meet either here, at your place, or somewhere in the middle like Gainsville. :)

 

You bet, just let me know what works and when for you. I am auditing an international terrorism class this semester that meets on Wednesday, and other than work I have time hither and fro.

Link to comment
Share on other sites

  • 2 weeks later...

Today marks my first major milestone: a full assembly sound test demo. The final game will use retrocloud's Spectra2 arcade game framework, and as such so does this demo.

 

The cool thing is this has helped me understand more about what I am actually planning with the game, and build a repertoire of routines I will need to support it. (Not to mention learning the frustrating and hard lesson of byte operations and arithmetic comparisons in the TMS9900!! :mad: ) Once I load this into my SuperSpace II cartridge I will have a chance to see how it will work on real TI hardware. Of course a number of important base-line features are not operating, like this demo uses graphics mode (just easier right now) while the final release will use bitmap mode. Over time the demo will improve in sound, graphics, and playability. As the demo is around 7k I know that I will soon be faced with the bank-switching dilemma.

 

Using this demo is easy: just move up or down with the joystick or appropriate keyboard keys and hit fire to play the selected song. The keyboard is sticky, so holding the button down for too long will cause the song to stutter-start. I am not certain that I will correct that for the game as immediate repeat of input is a practicality.

 

A big shout-out to retroclouds for indurring and encouraging my pestering these past few days while I work to get this done. I am also hoping that even with my novice 9900-Fu I can provide valuable feedback to help make the next release of Spectra even better.

 

So, without further ado, I present the Arkanoid sound test cartridge.

sound_test_cartridgec.bin

sound_test_cartridgec.rpk

  • Like 4
Link to comment
Share on other sites

Congratulations on the first milestone! It was fun exchanging ideas and me taking a look at some assembly language code I wrote about 4 years ago (crazy how time flies by).

And yes, providing feedback certainly is valuable. Please keep them coming.

 

A couple of years ago I pulled out the 6502 ML support framework I wrote for my Commodore 64 BBS program. It blew my mind. I even went through and bummed a few more bytes out of some of the routines -- just for fun! (What the hell is wrong with me?!)

 

It seems like I just got my Pitfall cartridge not that long ago but, yeah, it has been a couple-few years. One big thing I am pushing for with this game is to run on a bare console. I am not even considering a disk release for Arkanoid, though I have some other game ideas that I am pretty certain will be disk and 32k rather than cartridge.

 

I am, however, somewhat disappointed that I am not able to use E/A on a real TI to develop this game. Yes, I know, it seems crazy and futile, but I wanted to develop the same way I would have had to had I written this back in the day. Instead I am using WinAsm99 and Classic99's debugger (totally invaluable, thank you Tursi, and I have a suggestion for the break-points) as my development system is on a desk I cannot get to at the moment. Plus reading about the E/A debugger make my eye twitch a little. As disappointed as I may be, it is out-weighed by the ability to step through a program while watching memory contents and register values in "real-time."

 

I am about to drop dead, so I am hitting the sack. Later on I will post up my newbie experience with 9900 byte operations (did I mention already that they SUCK?) and non-logical (arithmetic) comparison operations. And how aggravated I am that the SWPB instruction does not set any status bits.

 

Good night, all.

  • Like 3
Link to comment
Share on other sites

Those sound effects are very nicely done. Beyond Rasmus' games and some exceptions there are very little games that do sound well and a lot of them resort to the same tired old, flat sounding bleeps and bloops. Please post as much as you can as you make progress, I love following these development diaries!

  • Like 3
Link to comment
Share on other sites

 

I am about to drop dead, so I am hitting the sack. Later on I will post up my newbie experience with 9900 byte operations (did I mention already that they SUCK?) and non-logical (arithmetic) comparison operations. And how aggravated I am that the SWPB instruction does not set any status bits.

 

Concerning byte operations, do they suck for the same reasons for us? :-) For me, it seems a BAD idea to include byte operations in a 16-bit system which has no way of splitting the data bus, so we have this silly read-before-write.

 

As for SWPB, maybe SRC could be an alternative? What status bits do you want to have?

Link to comment
Share on other sites

Great sounds, and very nice to see a new assembly game being developed. I hope this will inspire others to get started on assembler.

 

Assembler is not all that terrible. I did a little bit on the Mini Memory (simple stuff, like loading fonts and screen data, mostly,) and then moved on to the 6502 for which I generated a metric shyte tonne of code.

 

It is extremely helpful to have a base upon which to start, and in my case I am using Spectra2. Even better, I am helping to improve it while I learn. While I did want to develop on "real iron," the tools available to get people started today are phenomenal. I am using WinAsm99, but I understand there are at least a hand-full of other options for assembling. Plus the tools people have produced to assist in the development process. In the old days you would be best to have a team of at least a few people, it is possible to crank out a game or two on your own, even for newbs like myself: if I stick with it I could see a game a year.

 

 

Concerning byte operations, do they suck for the same reasons for us? :-) For me, it seems a BAD idea to include byte operations in a 16-bit system which has no way of splitting the data bus, so we have this silly read-before-write.

 

As for SWPB, maybe SRC could be an alternative? What status bits do you want to have?

 

 

Yep. SRC. Otherwise all you can really do is MOV Rx,Rx

 

I looked at SRC, and I believe you are both correct: it will do exactly what I want. Primarily, I want to be able to swap the byte and test for zero (JNE/JEQ.) As well, working with bytes has been a major pain in the ass, and I have been using code like this:

 

 

 

CAPMTN    CLR  R0                * Prepare R0 for byte operations
          CLR  R1                * Prepare R1 for byte operations
          MOVB @CAPPOS,R0        * Capsule motion: fetch capsule position
          MOVB @CAPDST,R1
          SWPB R0                * Put high-order byte in lower-order position
          SWPB R1
          C    R0,R1             * Compare position and destination

 

Especially when I need to increment or decrement a byte. But SRC gives me a much more elegant solution and I will work to implement that in all of the really obnoxious places.

 

 

Those sound effects are very nicely done. Beyond Rasmus' games and some exceptions there are very little games that do sound well and a lot of them resort to the same tired old, flat sounding bleeps and bloops. Please post as much as you can as you make progress, I love following these development diaries!

 

One thing I wanted to do with Arkanoid is bring a dimension to TI game-play that we did not see a lot of before. Sound is a huge one, and graphics is another. I will not be doing any scrolling, an aspect which Rasmus has down to an art, but I would like my graphics to be more colorful and fluid than some of the TI-era releases (with many notable exceptions from third parties.)

 

I will say it again, I know I have a wealth of resources here for which I am very grateful. I know if I run into any problems or have any questions I will get many answers and much assistance.

 

Feels good, man.

Link to comment
Share on other sites

CAPMTN    CLR  R0                * Prepare R0 for byte operations
          CLR  R1                * Prepare R1 for byte operations
          MOVB @CAPPOS,R0        * Capsule motion: fetch capsule position
          MOVB @CAPDST,R1
          SWPB R0                * Put high-order byte in lower-order position
          SWPB R1
          C    R0,R1             * Compare position and destination

I believe this should do what you want...?

 

          CB @CAPPOS,@CAPDST

The byte support is often irritating, but, it's relatively complete. One of the great things coming to the 9900 from the 6502 (which I also did!) is the power of direct memory addressing being in nearly every instruction.

 

Also, in your example code, the SWPBs were unnecessary, unless you are trying to treat signed values as unsigned on purpose (which sounds as confusing as it is, and is probably not a concern). The reason I mention this is because the arithmetic greater than flag may be set differently in the two examples for numbers greater than >80 (in your code, neither number will EVER be negative, but if you don't SWPB or use my direct CB, then values >80 or greater are considered negative. This only matters if you're using the arithmetic conditional jumps, and if so, is easily fixed by changing to the logical ones).

  • Like 1
Link to comment
Share on other sites

Also, to expand on Willsy's comment, if you just want to test a byte for zero, MOVB R1,R1 (ie: move the byte to itself) will set all the condition flags for just that byte. You can also MOVB @MEMORY,R1 to set the flags the same way. (I've seen MOVB @MEMORY,@MEMORY used, which works, but feels too wasteful of cycles to me. Still, if you have no registers free, and you are sure writing will have no side effects (ie: bankswitch), it works too. ;) )

  • Like 1
Link to comment
Share on other sites

Yeah, I should have put that in. The two following instructions are JEQ and JGT. This was my second complaint about byte handling that CB is arithmetic. It is a little counter-intuitive to me as I would expect the high-order bit of the word to negate, but not the high-order bit of the byte in a word. But then again, I can see how that behavior would be desired. Just not by me ;)

 

I completely neglected the idea of comparing memory contents. That is a big thing I like about 9900 versus 6502: the ability to work with two memory addresses in one instruction, rather than having to load one value into a register and then perform a store, compare, add, etc. IIRC and undocumented instructions notwithstanding, only INC and DEC and actually directly operate on and modify a memory location in 6502.

 

So tonight I am planning my next pre-release milestone. I want to have the Vaus on-screen and bouncing the ball around. This will test controller motion and acceleration, collision detection, directional and velocity shifting, and sound triggers.

Link to comment
Share on other sites

Fixing the CLR, SWPB, and CB mess, my motion routine now becomes

 

 

 

CAPMTN    CB   @CAPPOS,@CAPDST * Capsule position = destination?
          JEQ  CAPEND          * Yes, no motion
          MOVB @CAPPOS,R0      * Fetch capsule position
          MOVB @CAPDST,R1      * Fetch capsule destination
          SRL  R0,8            * Right-justify bytes
          SRL  R1,8
          C    R0,R1           * Compare position and destination
          JGT  CAPUP           * Destination greater,
          INC  R0              *  increase position
          JMP  CAPSAV
CAPUP     DEC  R0              * Destination more, decrease position
CAPSAV    MOV  R0,TMP1         * Move Y-pos to TMP1
          SWPB R0
          MOVB R0,@CAPPOS      * Save updated capsule position
          LI   TMP0,>0304      * Letter Y-pos in SAL
          BL   @XVPUTB         * Put byte in VDP RAM
          SWPB TMP1            * XVPUTB swaps MSB,LSB
          LI   TMP0,>0308      * Capsule Y-pos in SAL
          BL   @XVPUTB         * Put byte in VDP RAM
CAPEND    B    @SLOTOK         * Exit thread

 

 

 

EDIT: would this change give any benefit?

 

 

          JGT  CAPUP           * Destination greater,
          INCT R0              *  increase position
CAPUP     DEC  R0              * Destination more, decrease position
CAPSAV    MOV  R0,TMP1         * Move Y-pos to TMP1

 

 

Link to comment
Share on other sites

CAPMTN    CLR  R0                * Prepare R0 for byte operations
          CLR  R1                * Prepare R1 for byte operations
          MOVB @CAPPOS,R0        * Capsule motion: fetch capsule position
          MOVB @CAPDST,R1
          SWPB R0                * Put high-order byte in lower-order position
          SWPB R1
          C    R0,R1             * Compare position and destination

 

Yep. As Tursi suggests, CB @address,@address your friend here. It's quite an expensive instruction (it has to fetch the two adresses to compare, then go fetch their contents, then do the compare) but it'll be faster (and takes less memory space) than your routine above. In fact, there's quite a bit of functionality packed into that little CB instruction ;-)

 

The other point I wanted to make: There's no need to use the CLR instructions if you change the SWPB instruction to a SRL instruction.

 

I think people just get into the habit of zeroing a register with CLR and using SWPB for byte ops, because that's what we pick up from examples, but you could do:

 

 

MOVB @address,R1
SRL R1,8

 

That moves the data in the upper 8 bits to the lower 8 bits, zeroing the upper 8 bits as it does so. So pre-clearing with CLR is now redundant. SWPB (in my experience) is required relatively rarely. :-)

  • Like 1
Link to comment
Share on other sites

Also remember that you can calculate the direction by simply subtracting position from destination. In Assembler you may have to watch when the difference equals zero. Apart from the difference having a sign bit indicating direction, the value (difference) can also be used to make it speed up if the destination is far away.

 

Here's a little XB demo. Activate one of three lines with the line no. 160 for different effects.

 

! SETUP
100 CALL CLEAR
110 CALL SPRITE(#1,ASC("D"),2,100,100,#2,ASC("C"),2,100,100)
! MOVE DESTINATION
120 CALL JOYST(1,X,Y)
130 CALL MOTION(#1,-Y*4,X*4)
! GET POSITIONS OF DESTINATION AND CAPSULE
140 CALL POSITION(#1,DY,DX)
150 CALL POSITION(#2,CY,CX)
! MOVE CAPSULE TOWARDS DESTINATION
160 CALL MOTION(#2,DY-CY,DX-CX) ! ORGANIC/DEACCELERATES
! 160 CALL MOTION(#2,SGN(DY-CY),SGN(DX-CX)) ! SLOW BUT SPOT ON
! 160 CALL MOTION(#2,SGN(DY-CY)*8,SGN(DX-CX)* ! WOOPY
170 GOTO 120
Link to comment
Share on other sites

 

That moves the data in the upper 8 bits to the lower 8 bits, zeroing the upper 8 bits as it does so. So pre-clearing with CLR is now redundant. SWPB (in my experience) is required relatively rarely. :-)

 

Also, SWPB is surprisingly slow on the 9995: It takes 13 cycles (all operands on-chip) while an addition (A) takes 4. Since the shift operations take 5+C cycles (C=shift count), it seems as if it is implemented on micro-level as SRC.

Link to comment
Share on other sites

 

Also, SWPB is surprisingly slow on the 9995: It takes 13 cycles (all operands on-chip) while an addition (A) takes 4. Since the shift operations take 5+C cycles (C=shift count), it seems as if it is implemented on micro-level as SRC.

I was actually wondering the same thing as I was writing my post! Interesting though that SWPB has its own opcode. SWPB could have been implemented at the assembler level as a simple macro, as NOP is. Or even omitted completely. It's effectively redundant as SRC can do the same thing.

Link to comment
Share on other sites

 

Also, SWPB is surprisingly slow on the 9995: It takes 13 cycles (all operands on-chip) while an addition (A) takes 4. Since the shift operations take 5+C cycles (C=shift count), it seems as if it is implemented on micro-level as SRC.

 

Perhaps your point, but for the 9900 with 1(?) wait sate for register access, SWPB R1 takes 13 cycles, whereas SRL R1,8 takes 31 cycles!

 

...lee

Link to comment
Share on other sites

...

The other point I wanted to make: There's no need to use the CLR instructions if you change the SWPB instruction to a SRL instruction.

 

I think people just get into the habit of zeroing a register with CLR and using SWPB for byte ops, because that's what we pick up from examples, but you could do:

MOVB @address,R1
SRL R1,8

That moves the data in the upper 8 bits to the lower 8 bits, zeroing the upper 8 bits as it does so. So pre-clearing with CLR is now redundant. SWPB (in my experience) is required relatively rarely. :-)

 

But, with 1 wait state,

SRL R1,8

takes 31 cycles, whereas,

CLR R1
...
SWPB R1

takes 26 cycles. It takes 2 bytes more of program space, but it is faster.

 

With 2 wait states, it's almost a wash at 34 and 32 cycles, respectively—but still, faster.

 

...lee

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

 

But, with 1 wait state,

SRL R1,8

takes 31 cycles, whereas,

CLR R1
...
SWPB R1

takes 26 cycles. It takes 2 bytes more of program space, but it is faster.

 

With 2 wait states, it's almost a wash at 34 and 32 cycles, respectively—but still, faster.

 

...lee

 

What about when addressing 16-bit PAD RAM? I thought there is no wait-state in that area. At the very least, say in 8-bit space one could look at the longer method as you would look at unrolling a loop -- that is, longer and sloppy-looking, but potentially (much) faster.

Link to comment
Share on other sites

 

What about when addressing 16-bit PAD RAM? I thought there is no wait-state in that area. At the very least, say in 8-bit space one could look at the longer method as you would look at unrolling a loop -- that is, longer and sloppy-looking, but potentially (much) faster.

 

With 0 wait states, they are 28 and 20 cycles, respectively.

 

...lee

Link to comment
Share on other sites

 

You can also load a byte directly into a register low byte. This is a little bit faster than loading into high byte and swapping:

WRKSP  EQU  >8300               * Workspace
R0LB   EQU  WRKSP+1             * R0 low byte

       CLR  R0
       MOVB @CAPPOS,@R0LB

 

By accessing the register with symbolic addressing, you increase the time by 8 clock cycles and 1 memory access, which, if scratchpad RAM access is 0 wait states, is just +8 cycles. That may be cheaper than with SWPB; but, I think I'm going to wait for someone like @Tursi to weigh in on this before I make more of a fool of myself because I don't remember whether wait states are variable, depending on the width of the memory bus, or always the same for all CPU memory access on the TI-99/4A. I also don't remember what the worst-case number of wait states on the 4/A is, 1 or 2. :dunce:

 

...lee

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