Jump to content
IGNORED

Assembly on the 99/4A


matthew180

Recommended Posts

If A and B are between 0 and 255

XB: C=A :: A=B :: B=C

AL: SWPB R0

 

Not sure what you were going for on this one...? I think you forgot to mention that A is the high byte and B is the low byte of R0.

 

XB: C=A :: A=B :: B=C

AL: MOV R0,R2

AL: MOV R1,R0

AL: MOV R2,R1

 

I assume you were swapping A and B? You forgot one version, which only needs two registers:

AL: XOR R0,R2

AL: XOR R2,R0

AL: XOR R0,R2

 

Adamantyr

Link to comment
Share on other sites

If A and B are between 0 and 255

XB: C=A :: A=B :: B=C

AL: SWPB R0

 

Not sure what you were going for on this one...? I think you forgot to mention that A is the high byte and B is the low byte of R0.

 

Well, I was going on Owen's original example, so I assumed he understood that SWPB was dealing with bytes, plus the comment I made that A and B had to be between 0 and 255 to even consider the example.

 

I assume you were swapping A and B? You forgot one version, which only needs two registers:

AL: XOR R0,R2

AL: XOR R2,R0

AL: XOR R0,R2

 

Yeah, I didn't forget, I just think it is confusing unless you have seen the trick before. But that's just my opinion. I've seen this question asked in an interview setting before, as if knowing the xor trick put the candidate in some elite class of programmer or something. It does save a register, but it is still three instructions and won't be any faster.

 

However, if you do like stuff like that, then I highly recommend the book Hacker's Delight which is full of tricks like that. Here are 3 more ways to swap registers without using a 3rd:

 

x = x + y

y = x - y

x = x - y

 

x = x - y

y = y + x

x = y - x

 

x = y - x

y = y - x

x = x + y

 

Matthew

Link to comment
Share on other sites

Yeah, I didn't forget, I just think it is confusing unless you have seen the trick before. But that's just my opinion. I've seen this question asked in an interview setting before, as if knowing the xor trick put the candidate in some elite class of programmer or something. It does save a register, but it is still three instructions and won't be any faster.

 

Oh yeah, it falls into the "look at me, the clever fellow" category for programming tricks. It's an interesting lesson on binary numbers and how to transform them, though, which is where I see the value with it.

 

Yes, I have been asked it in interview questions before... If you claim to know assembly language at all, you should expect the question for certain. I'm just glad that the "brain teaser" interviews are being phased out... they don't do it at Microsoft anymore, although Google continues the practice. They think it helps them find "out of the box thinkers" when all it really does is find people who know how to memorize a LOT of puzzle solutions online, and not necessarily people who work well in a team, have a good work ethic, etc.

 

Adamantyr

Link to comment
Share on other sites

However, if you do like stuff like that, then I highly recommend the book Hacker's Delight which is full of tricks like that.

 

Just checked out Hacker's Delight book. Interesting stuff.

This is the fun part of our TI programming forum. It's not just about programming TI games.

You learn so much along the way :)

Link to comment
Share on other sites

Question for you Matthew--- obviously saving a bunch of full screens into memory will chew up memory... In the menu screen I demo'd on the Beryl thread, I am considering not changing the whole screen upon menu selection... Maybe just the left side of the screen would change. So--- it would be simple to redraw the whole screen, but what if I only need to change half of it? Could the new data be placed on the screen (written

over the old data) using the same method as my viewport? If it takes up the whole right side of the screen--- something like

 

x=1

y=begin screen draw data (16 chars)

-BEGIN loop

display at(x,1):y;

x=x+1

y=y+16

goto begin

 

that's sloppy junk, but you see what I'm saying... Is that appropriate in assembly or is simply a full screen draw the preference? I'm just worried that with extensive menu screens, multiple screens saved into RAM could eat what I have available. Of course, if I was doing a cart---- there's enough room for ALL the engine stuff and menus right there in 64k of ROM.

Edited by Opry99er
Link to comment
Share on other sites

Question for you Matthew--- obviously saving a bunch of full screens into memory will chew up memory... In the menu screen I demo'd on the Beryl thread, I am considering not changing the whole screen upon menu selection... Maybe just the left side of the screen would change. So--- it would be simple to redraw the whole screen, but what if I only need to change half of it? Could the new data be placed on the screen (written

over the old data) using the same method as my viewport? If it takes up the whole right side of the screen--- something like

 

x=1

y=begin screen draw data (16 chars)

-BEGIN loop

display at(x,1):y;

x=x+1

y=y+16

goto begin

 

that's sloppy junk, but you see what I'm saying... Is that appropriate in assembly or is simply a full screen draw the preference? I'm just worried that with extensive menu screens, multiple screens saved into RAM could eat what I have available. Of course, if I was doing a cart---- there's enough room for ALL the engine stuff and menus right there in 64k of ROM.

 

Ah, I see you've discovered that menus and displays can really chew up memory. :) It's a rather surprising and unwelcome revelation, isn't it?

 

There's a few techniques for menu-driven interfaces on the TI that minimize the footprint:

- Use a basic "menu template" that is identical for all usage, and just alter the text

- Create routines to auto-generate the various menu elements, including text and the bounding boxes

- Store your menus as image files on disk and load them as needed directly into the screen buffer. Each takes up around 1k on the disk (768 bytes/3 sectors of screen, plus an additional sector/256 bytes for the file header)

 

For example, I wrote a short routine in my CRPG to generate the bounding boxes, with registers used to pass the starting location, width, and height. That can be reused on a blank screen easily enough. Text placement still requires you to store the text somewhere and then write it out to screen.

 

Probably the largest issue to overcome is minimizing your interaction model. Basically, you want to figure out everything you want menus to do, and then find the most cost-effective way to do it in code that doesn't require a lot of special casing. For example, having the same option in the same place on multiple screens can really help cut down on the need for dynamic processing. Generally with game programming, you're either trading off data-space for faster performance, or code-space for reduced resources. And menu generation is a great example case for both methods.

 

Adamantyr

Link to comment
Share on other sites

Similar to what Adam was talking about, what you need to do is make a generic menu drawing routine, then pass it a list to display. The selection for a specific menu item could be part of the list data. So your menus only take up as much room as the text to be displayed and no more.

 

You need to get out of the mindset of using the screen to contain any data. The screen is the end result and you should always know what you put there without having to read data back from the screen buffer. This is no different than the map data sitting in RAM that is used to update the display.

 

Always being able to redraw any part of the screen means you don't have to worry about what you might be clobbering when you draw a menu. When the menu is done, simply redraw what was there before. This is also the basic principle of a "windowed" operating system. You have to be able to redraw the contents of your window any time the OS says. So, always think in terms of data to the screen and never the other way around.

 

Matthew

Link to comment
Share on other sites

Yes, and sometimes ...

 

It's nice to read the screen for simple collision detection.

 

If you plan on doing a cartridge with no memory expansion, then VDP has often to be utilised somehow.

 

If design and VDP memory allows for it, you could have several screens in VDP at one time, and simply switch between them.

 

:cool:

Link to comment
Share on other sites

Thanks guys--- got the messages loud and clear. Too bad we don't have 64k of VDP RAM. :)

64K !? - Let's just see how much we can get with the 16K VDP (current Beryl config ?) ...

 

We try and use the regular places first. We set the screen at >0000. The colors are set at >0300. The sprite attributes are set at >0380. We want to separate character and sprite pattern tables to have full 256 entry ranges. We set character patterns at >1000. And we set sprite patterns at >1800. This gives us the following ranges:

 

>0000 - >02FF Screen (1)
>0300 - >031F Colors
>0320 - >037F Free space (could hold 3 extra color sets, but then they are merely 32 bytes each)
>0380 - >03FF Sprite attributes
>0400 - >07FF Free space
>0800 - >0FFF Character patterns (256 characters)
>1000 - >17FF Sprite patterns (64 doublesized sprites)
>1800 - >3FFF Free space

Now 256 different characters is actually a lot. Let's see how many extra screens we can cram in there.

 

>0400 - >06FF Screen 2
>1800 - >1AFF Screen 3
>1C00 - >1EFF Screen 4
>2000 - >22FF Screen 5
>2400 - >26FF Screen 6
>2800 - >2AFF Screen 7
>2C00 - >2EFF Screen 8
>3000 - >32FF Screen 9
>3400 - >36FF Screen 10
>3800 - >3AFF Screen 11
>3C00 - >3EFF Screen 12

You can flip/switch instantly between 12 screens !

 

My Luke demo flips thru 8 screens producing easy and instant full screen animation. I know - remember to turn down the volume.

 

 

 

:cool:

Edited by sometimes99er
Link to comment
Share on other sites

So my actual map data for the world in play would be wiped from memory and replaced with menu data. I assume from cart... So, fast. Then when menu time is over, the map data is reloaded from cart and the screen returns to it's previous position on the overall world map... This sound like what you're talking about? Right now I'm looking at 4.5-6k per world.... Each world will be separate from one another, so only one world and one set of parameters are loaded at once into memory. This leaves me with some free space in VDP RAM for menu screens, but I'm not sure how much will be used for other things, like soundlists for each world and whatnot. However, if we're running from cart, much of that data can be in cart ROM and loaded immediately--- menus could be stored in cart ROM,

and then called upon when needed. Fortunately, I've got no predispositions for structure because I don't know enough... But when it comes time to hit this stuff up, I'll be sure to be studied up and ready to go!! :)

Link to comment
Share on other sites

There's a 256 bytes free between each of those extra screens, if you want to put something there (sound effects, music, speech ...). But then again you would load them from ROM (assuming a cart only solution (no disk access)). Again there's the trickery of ROM page switching, so it'll be a matter of individual taste etc. If you're not using all 12 screens, then there's even more continuous VDP available.

 

:cool:

Edited by sometimes99er
Link to comment
Share on other sites

So my actual map data for the world in play would be wiped from memory and replaced with menu data. I assume from cart... So, fast. Then when menu time is over, the map data is reloaded from cart and the screen returns to it's previous position on the overall world map... This sound like what you're talking about?

 

Not exactly.

 

Most people think about drawing the screen once, then they worry about what they destroy when they animate, draw a menu, or whatever. What I'm talking about, and what the game loop does, is to force you to think about drawing the screen over and over, every 60th or 30th of a second. Of course there are a few practical limitations we have to worry about since our hardware speed is limited, but that's the basic idea.

 

Most people have this kind of idea when making a game or something:

draw the screen
save the screen under the player
loop:

 draw the player

 user input:
   if position updated:
     restore screen where player is
     save the screen under the player

end loop

 

So you spend your time saving and restoring chunks of the screen you are destroying when you move. However, if you incorporate your drawing into the loop, you no longer have to worry about it:

loop:

 draw
   screen
   objects
   mobs
   bullets
   player

 user input

 mob AI

 update
   objects
   bullets

end loop

 

So what you get in the second method is a way to animate and move things without worrying about what was there before. This gives you the power of "layers", just like in a paint program. Notice in the second example how things don't stop just because there is no user input. Doing this means that objects on the map can still be animated and such, mobs still move, bullets still fly, etc.

 

For the menu, it simply becomes part of your draw routine. You don't just put up a menu and do nothing until the user makes a selection. How will you animate the cursor, or flags blowing in the wind, or the water, etc. if your code is tied up in a loop waiting for user input? The menu just becomes something else that you draw and the selection is taken care of in the "user input" part of the code.

 

As for drawing the actual menu, I suggest a subroutine that you pass a list of text and set an associated indicator in your state machine so your user input code knows what keys are valid at that time. You could also use that indicator to skip mob AI (which prevents them from moving), yet still allows the background to animate and such.

 

In the map scroll code I sent you, the player is moved and tracked separately from the map data. Same idea. However, in FlyGuy II, I used the "save and restore" method to move the spiders and FlyGuy, but those are just single characters and my screen was not dynamic, i.e. not a game world or sorts, so I had no reason to redraw it every frame. Every situation is unique, and part of programming is choosing the right solution for the problem.

 

Also, a lot of game programming is fake, like Hollywood, it is just a facade. In modern 3D FPS games, the world is paper thin and looks really bad if you managed to get *behind* a surface you are not supposed to. Our mobs are not *really* smart, we just make them look that way with tricks. That's just how it goes. It is all just a balancing act between what you have to work with and what you want need to do.

 

Matthew

Link to comment
Share on other sites

I need to get out of the XB mentality.... As you said, Matthew--- in XB, you set up the screen and then move everything else on top of that screen.... If we updated the screen every cycle, it would make for more precision and alot more control. :) Someone here should make a "ninja gaiden" game for the TI.... I'd love to see what you gurus come up with. :)

Link to comment
Share on other sites

So-- just about done here in Key West/-- it has flown by!!!! I've had very little time for programming, but plenty of time to think. :) I have a week in Georgia next week, then it's home to get back to The Reichardt state of mind. In the meantime, here's my question of the night...

 

Q: When I set up the KSCAN to check for both joystick and keyboard input, should I set it up all as one routine or two separate ones? For instance, right now, it's set up to JUST check for joystick. However when played on the real TI, it would be probably preferable to use arrow keys. I want to keep both in for emulation AND real TI play. Should I just set up another routine alongside the joystick KSCAN, or is there a better way to check for both?

 

Hope this makes sense.... I'm not in my correct state of mind for this kind of thinking. :)

Link to comment
Share on other sites

What I plan on doing is developing, and subsequently writing about, a key scan routine that works similar to what you get in a modern OS or via a "game" library (like SDL).

 

First, every key "press" is actually two events and a status. When a key is pushed down is an event, when the key is released is an event, and the status tells how long a key has been pressed (to provide repeating for example) and what the "current" state of the key is (up or down).

 

When it comes to user input, you have two basic types:

 

1. Menu selections and typing

 

2. Movement keys / joystick / mouse / etc.

 

For selections and typing you typically only care about "key up" events. For movement you want to react only to the key's current status of "up" or "down". To support things like upper and lower case, and such, you can also test to see if any of the modifier keys (shift, ctrl, fcnt, etc.) are also in a "down" state.

 

Personally I suggest you do something like what Adam suggested over in the Beryl thread, i.e. a single subroutine that is used to check the keyboard and update the state of keys in an array. The array will contain any keys your application care about, which may end up being all the keys on the keyboard. This is very similar to what I plan on developing, but my ideas are still theory at this point. I might not have enough CPU time or memory to realize the idea.

 

Joysticks are separate from the keyboard but checked the same way, that is, by polling via the 9901. Thus you could include checking the joysticks in the keyboard subroutine and updating that information as well. In action games like FlyGuy II where you only need to test the joystick, you can do it very quickly by setting the 9901 to always poll the joystick, then checking the joystick only takes a single instruction for each direction.

 

I will be getting to the keyboard eventually, but probably not as quickly as you want. I'll try though.

 

Matthew

Link to comment
Share on other sites

Thanks Matthew!!! I won't be needing that routine until I get all the logic figured out. I'll give it a whack myself too, so more than likely I'll have something rough but functional to compare your routine to. Similarly to how the scroll routine went down. I like the challenge of doing it myself and then learning how to optimize by reading the suggestions of you and Adam and others. Thanks again for being so accomodating. :)

Link to comment
Share on other sites

What I plan on doing is developing, and subsequently writing about, a key scan routine that works similar to what you get in a modern OS or via a "game" library (like SDL).

First, every key "press" is actually two events and a status. When a key is pushed down is an event, when the key is released is an event, and the status tells how long a key has been pressed (to provide repeating for example) and what the "current" state of the key is (up or down).

 

As a game programmer, can I note that having up/down events to deal with really sucks? 99 times out of 100, you just want to know, "Is the key down NOW?" Event messages force you to track that state yourself, which is kind of a pain. Since the TI keyboard is scanned by the CPU, that means your library will need to track the current state in order to report up/down events, which in games will usually be translated back to a current state. Of course, you could just have a "get state" function as well, but I question the value of the events plan?

 

The reason modern OSs work on events instead of current state has less to do with usability and more to do with hardware -- the PS/2 keyboard works in just that way, by sending up and down events, and even repeat information. Interestingly, USB keyboards go back to giving a "current" report (which is translated today into the old system by software).

 

Just my two bits, if you're intending that as a game library. :)

Link to comment
Share on other sites

I was not planning on implementing an "event" system, just using terminology to bring into focus the idea that there is more to a key being pressed than just "was a key pushed".

 

I agree totally agree, most of the time you just want to know if the key is currently pressed. However, depending on how often the key scan routine is called (10 to 30 times a second is typical), a "key up" indicator is necessary for text input. No matter how you do the input, you have to track if the key was pushed the last time you called the scan routine.

 

Maybe having a mode associated with the key scan routine would be a good idea. That way if you only care about movement information, the routine simply updates the up / down status of the keys. Otherwise, if you call it with "text input" mode, it will track the "up" state and maybe even how long the key has been pushed.

 

I don't know, I have not totally worked it all out yet. I'm certain that I won't be using KSCAN though, since after reading the ROM code (and Sometimes' post on the matter) I know KSCAN has two delay loops which certainly attribute to its slowness. I understand they do it for debouncing, but there are better ways.

 

Matthew

Link to comment
Share on other sites

Finally got home from 3 weeks on the road!!!! Yay! Home never felt so good. Well, on the 22 hour drive home, I made some mental notes on moving forward. Aside from the big development document which is starting to take shape, I've decided to go piece by piece in a new development blog. The one I have on Opry99er.com lacks structure, good format templates, and everything else I need. I spent an hour on Adamantyr.com and really fell in love with the organization and structure there. It inspired me, really. :) Thanks Adam. Anyway, I'll be posting more frequently now that I'm home and my design and programming will kick into a new gear. I discovered that I won't be requiring much assembly advice for a while.... Too much planning and writing to do before I get back to coding anything!!! :) Im new to this kind of programming and this kind of game, so it took me a while to figure out the necessary order of operations. Thanks to Mark, Adam and you, Matthew. I've learned a valuable lesson and it didn't take me screwing up months worth of work to learn it!!! :)

Link to comment
Share on other sites

Edit: Disregard. I started reading the EA manual and found the "R" option that needs to be set for the assembler to define the R registers. It's working now.

 

I just started to study this thread. FYI, I'm a total noob assembly programmer. I cut/pasted the first three blocks of code from post #1 and transferred to my TI-99/4A via a CF7+. I am using a E/A cartridge with downloaded E/A disk images written to the CF7+. I can assemble then Load/Run the first two code blocks. However, the third gives errors. I think it doesn't like "R0", "R1", and "R2" in the statements. Is this expected? Do I need to define those myself? Or should the assembler be working those out by itself? Want to make sure I'm not doing something wrong before moving on.

 

Looks like the rest of this thread will be a great read. Thanks.

Edited by chris36
Link to comment
Share on other sites

Hey Chris36, welcome to 9900 assembly, it's great to have you on board!

 

I have not yet written the walk through for assembling a project, but I see you figured it out. :-) That's the best way to learn anyway (researching and finding your own answers), and when programming in assembly you will do that a lot!

 

Hmm, it has been a while since I posted anything new, I guess I better get going with something. If you have questions or want to see specific examples, don't hesitate to ask.

 

Matthew

Link to comment
Share on other sites

Beware self-modifying code... I often do things like this versus using a stack... ;)

 

 
SUB1   MOV  R11,@SUB1RT+2
      ...
SUB1RT B  @0

 

 

Matthew - this is an awesome thread you've got running :) This is giving me some great ideas for a few programs I'd have trouble writing without getting out of my dsr/utility/input-driven serial-event processing mindset :)

 

DEFINATELY beware self modifiying code if you are writing code for a cart ;-) :roll:

 

One alternative to self-modifiying code would be the X instruction, since it can take the op-code in a register IIRC:

    LI R0,>0582  ; OPCODE FOR "INC R2"
    LI R1,14     ; COUNT
LOOP X R0         ; EXECUTE THE INSTRUCTION IN R0
    INC *R0      ; SET NEXT REGISTER
    DEC R1       ; FINISHED
    JNE LOOP     ; JUMP IF NOT
[16 bytes]

 

The above being functionally equivalent to:

INC R2
INC R3
INC R4
INC R5
INC R6
INC R7
INC R8
INC R9
INC R10
INC R11
INC R12
INC R13
INC R14
INC R15
[28 bytes]

Not the most useful example, but you get the idea :cool:

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