Jump to content
IGNORED

Can you help me with ASM?


Sergioz82

Recommended Posts

I'm also needing to learn how to use the"DATA" directive and understand what and how it does what it does.

I only know that I can make a sound table with DATA and set the sequence of values to proper orientation and when I slam it together it works and it's easy pretty much easy to see where an error hits because of the nature of laying out the data with commas, ",".  but I get confused when to use DATA and BYTE, as I think, but not sure, but I think the table can be made BOTH ways?

 

 

 

Link to comment
Share on other sites

The BYTE directive occupies 8 bits, one byte, or you can think of it as one character from >00 to >FF.  The DATA directive occupies 16 bits or two bytes, or a range from >0000 to >FFFF.

 

With the use of DATA directive, its location in memory is on an even word boundary, i.e., >xxx0, >xxx2, >xxx4, >xxx6, >xxx8, >xxxA, >xxxC, >xxxE when assembled.

The BYTE directive can be on either an even or odd boundary.  If an instruction follows the use of the BYTE directive, that next instruction is on an even word boundary.  If the BYTE directive assembled on an even boundary, it will actually use two bytes when assembled as something must be filled in following at the odd byte.  Think of this as wasted memory.  That wasted byte, depending upon the assembler/linker you are using, could have a random byte.  Thus, if you are going to be using several BYTE directives such as:

 

H00   BYTE >00

H01   BYTE >01

H0A   BYTE >0A

HFF    BYTE >FF

 

Then, you should list them together as a group so you do not waste memory.

 

With the above example, both of the following examples will reference the same byte.

 

START   MOVB  @H01,R1

            MOVB @H00+1,R1

 

Both statements above reference the same byte and place it in the MSB of R1 so that R1 is >01xx  where xx is unchanged from its previous value.

 

There is also the use of the TEXT directive.  It can be in even or odd length.  It too, if it is followed by another instruction, will be appended with a random byte if it is of odd length.  You can also have multiple TEXT statements together so you do not waste memory.

 

You will find something like this very common:

 

STRING1  BYTE  >08          Length of String

STRING2  TEXT 'Hello'  

CRLF        BYTE >0D,>0A,0

 

In the above example, you could have a routine that writes text to the screen that you pass to a routine such as the following:

 

              LI    R1,STRING1

              BL   @DISPLAY

 

In the case above, your DISPLAY routine would reference the pointer in R1 to STRING1.  It would know the string is 8 characters in length including the carriage return/linefeed, and termination character.  On the other hand, you could also do something like:

 

              LI   R1,STRING2

              BL  @DISPL2

 

In the second case, your routine at DISPL2 would read the string and would need to know to stop displaying text at a specific character such as >00, the nul character.

 

There are lots of ways to attack the same problem.


Now, what you want to be careful and not do is something like the following:

 

               DATA >0000

LENGTH   BYTE >03

               DATA >0D0A

               DATA 0

 

In this case, when the code is assembled, it will assemble as:

               

>0000,>05xx,>0D0A,>0000    Notice the xx is a random character depending upon the assembler/linker. 

 

This is because DATA forces things on an even boundary, and you had an even address location for LENGTH so it had to add a byte for the next DATA statement to be on an even boundary.

 

I hope this makes some sense.

 

 

  • Like 2
Link to comment
Share on other sites

3 hours ago, GDMike said:

I'm also needing to learn how to use the"DATA" directive and understand what and how it does what it does.

It creates space for at least one item of 16-bit wide data. It may, but doesn't have to, include a data item to load when the program is loaded in memory.

If you want some words with specific data, you can write

DATA 12,154,763,14,-2

Here you get five words with some values stored in them at load time.

Now if you only need space for five such items, you can use

BSS 10

This means that room is provided for 10 bytes, which is five words, but nothing is said about what the content is. BSS is for Block Start with Symbol, i.e. a data space with a label reference to the first word/byte.

There is also BES, which is Block Ending with Symbol.

STACK BES 20 is convenient for a stack, since the instruction set of the TMS 9900 is better suited for stacks growing towards lower addresses. The label will define the first byte/word after the block. Thus a push on a stack, which is implemented as

DECT SP

MOV @WHATEVER,*SP

will come to the right place, provided the register SP is set to STACK to begin with.

  • Like 3
Link to comment
Share on other sites

21 minutes ago, mizapf said:

I guess the BES is less interesting for us because we have a register auto-increment access, but no auto-decrement. Interestingly, the BSS and BES instructions are quite common: https://en.wikipedia.org/wiki/.bss

True. Auto-decrement would be very nice to have. 

Amazing what you can find on Wikipedia.

@apersson850 's example shows how BES makes creating some stack space that grows downward just a little bit simpler. 

Simpler is always better for my brain. :)

 

  • Like 1
Link to comment
Share on other sites

Again, thank you all for you help.

 

 

This is the game right now.

Movement is fine, ghosts chase and keep chasing when within 48 pixels from pacman otherwise they give up. I noticed they stop chasing also when pacman moves in another horizontal/vertical half of the screen, it's probably something in the chase routine I have to fix.
 

  • Like 7
Link to comment
Share on other sites

2 hours ago, Sergioz82 said:

I noticed they stop chasing also when pacman moves in another horizontal/vertical half of the screen, it's probably something in the chase routine I have to fix.

Sounds like it could have something to do with signed vs. unsigned comparison of bytes.

  • Like 1
Link to comment
Share on other sites

On 8/31/2022 at 8:05 AM, apersson850 said:

It creates space for at least one item of 16-bit wide data. It may, but doesn't have to, include a data item to load when the program is loaded in memory.

If you want some words with specific data, you can write

DATA 12,154,763,14,-2

Here you get five words with some values stored in them at load time.

Now if you only need space for five such items, you can use

BSS 10

This means that room is provided for 10 bytes, which is five words, but nothing is said about what the content is. BSS is for Block Start with Symbol, i.e. a data space with a label reference to the first word/byte.

There is also BES, which is Block Ending with Symbol.

STACK BES 20 is convenient for a stack, since the instruction set of the TMS 9900 is better suited for stacks growing towards lower addresses. The label will define the first byte/word after the block. Thus a push on a stack, which is implemented as

DECT SP

MOV @WHATEVER,*SP

will come to the right place, provided the register SP is set to STACK to begin with.

Very good explanation, I knew of BSS as it's really something you need to know how to handle, and BES I knew of, but figured everything worked that I needed with BES, so I never pursued using it, I really appreciate you laying out the DATA example here, I kinda saw that with the sound table reference, and I've seen it used in my PAB for filing, but I needed to see your explanation a bit to bring it together.. thx so much!

 

 

 

 

 

 

Link to comment
Share on other sites

15 hours ago, Asmusr said:

Sounds like it could have something to do with signed vs. unsigned comparison of bytes.

 

Now that you tell me this I think the problem might be in the collision detection:

 

I set up the routine as shown in Compute!'s Beginner's Guide but modified with an extra step: first it checks if ghost and pacman are within 16 pixels, if not it skips to a second comparison to check if they're within 48 pixels. If so it sets a ghost behavior byte to chase mode, if not it sets the byte to seek mode, then the ghosts movement routine will handle the behaviours.

In collision routine I calculate the distance by using S instruction and then ABS the result. Could this be the culprit? 

 

In ghost movement routine the chase mode is very simple, it just compares the coordinates of ghost and pacman then move the ghost up if pacman Y is lower, move it right if pacman X is higher and so on:

 

FCHASE

       LI R4,PACYX      Load R4 with address of Pacman position                                                        
       CB *R1,*R4        Compare it with ghost position - Y  (R1 has been previously loaded with the address of ghost position)                                                     
       JGT CHUP         if ghost Y is higher go set direction up                                                        
       JLT CHDWN      if it's lower go set direction down                                                          
       CB @1(R1),@1(R4) Compare it with ghost position - X                                                         
       JGT CHLFT        if ghost X is higher go set direction left                                                        
       JLT CHRGHT     if ghost X is lower go set direction right                                                          
       JMP FMOV       if coordinates are equal continue the movement routine with the previous direction                                                       

 

CHUP   MOV @DSU,R0  move direction up (>FF00) in R0                                                             
       LI R7,OCCSU, load R7 with eyes up character                                                              
       JMP FMOV      continue the movement routine                                                           
CHDWN  MOV @DGIU,R0   same as above for the remaining directions                                                          
       LI R7,OCCGU                                                              
       JMP FMOV                                                                 
CHLFT  MOV @DSX,R0                                                              
       LI R7,OCCSX                                                              
       JMP FMOV                                                                 
CHRGHT MOV @DDX,R0                                                              
       LI R7,OCCDX                                                              
       JMP FMOV           

 

 

Edited by Sergioz82
Correction in a code comment
  • Like 1
Link to comment
Share on other sites

@Tursi @Asmusr

I used the debugger to pin down the timer problem.

I also simplified the code as @apersson850 suggested.

I still had the problem by using DEC instruction. With debugger I noticed that high byte was decreased as well so I thought it might have something to do with carry. This perplexes me because I thought carry was set if DEC went below zero.. :roll:

Anyway I replaced DEC with AB instruction and now it works.  

To make the ghosts skip 1 cycle I had to use 02 as counter since lower byte is never zero outside this routine (in main loop I compare high byte with low byte and JNE beyond BLWP @ghost routine)  

 

FASPD DATA >0202

 

UPDTIM                                                                          
       AB @HFF,@FASPD+1 (before it was DEC @FASPD+1)                                                         
       JEQ RESTR1                                                               
       B *R11                                                                   
RESTR1 MOVB @FASPD,@FASPD+1                                                     
       B *R11    

 

  • Like 1
Link to comment
Share on other sites

2 hours ago, Sergioz82 said:

This perplexes me because I thought carry was set if DEC went below zero.. 

DEC works like this: Add FFFF to the source operand. Thus, you get a Carry=1 when the operand is 1 or higher. You only get Carry=0 if the source operand is 0, as the result is FFFF.

  • Thanks 1
Link to comment
Share on other sites

4 hours ago, Sergioz82 said:

I still had the problem by using DEC instruction. With debugger I noticed that high byte was decreased as well so I thought it might have something to do with carry. This perplexes me because I thought carry was set if DEC went below zero.. :roll:

Anyway I replaced DEC with AB instruction and now it works.  

You can only use DEC on a word.

  • Like 1
Link to comment
Share on other sites

28 minutes ago, Asmusr said:

You can only use DEC on a word.

Oh, ok.

In the manuals I use (Compute!'s and Fundamentals of TI-99/4A Assembly Language) it's not really specified it's a word instruction. They just say it decreases a memory location.

They both use even addresses in the examples though, I should have taken that as hint :)

 

 

24 minutes ago, Asmusr said:

I think @apersson850 was suggesting to use a word for your counter instead of a byte. Just use DEC on the word, and when it reaches zero you execute your routine (MOVFAN) and set to back to the start value. 

 

I got fixated on understanding why my byte counter (and labels) didn't work. 

Now I can move on and switch to words.

 

 

 

  • Like 2
Link to comment
Share on other sites

On 8/31/2022 at 7:47 PM, mizapf said:

I guess the BES is less interesting for us because we have a register auto-increment access, but no auto-decrement.

Not when you implement a stack. Since the auto-increment is post increment, you can't easily use it to push data on the stack.

Assume you have a stack that grows towards higher addresses, and you want the stack pointer to point to top of stack all the time. In such a case, push and pop has to be implemented like this:

PUSH    INCT SP
        MOV  R1,*SP
POP     MOV *SP,R1
        DECT SP

As you can see, we can't use neither the auto-increment nor auto-decrement (the latter because it doesn't exist). This increment has to be a pre-increment, and that also doesn't exist.

But with the stack growing towards lower addresses we get what we are used to.

PUSH    DECT SP
        MOV  R1,*SP
POP     MOV  *SP+,R1

Had there been a pre-decrement instruction (some processors have that too), then we could have used

PUSH   MOV  R1,*-SP
POP    MOV  *SP+,R1

But what we have is limited by the two bit address modifier and the rule that an index register can't be R0. Thus we have five general addressing modes, nothing more.

 

Yes, the ability to use the DEC and INC instructions was one of the reasons for why I suggested using a word for the counter, instead of messing with bytes. A word uses more space than a byte, but an instruction like DEC R1 is two bytes, AB @H01,@LSBYR1 is six bytes. Thus you waste much more space on using only 8 rather than 16 bits for the counter, already after executing one single instruction to handle the counter.

 

One of the advantages with assembly language programming is that you can control every detail the CPU provides for you. You can do whatever you like to do, as long as it's supported by the CPU. No interpreter or compiler is in the way, telling you that this task should be solved in a specific way.

While giving you the ultimate freedom, it also gives you an unlimited number of ways of doing things less optimal. Or downright stupid, sometimes. The only way to have a chance of knowing if the approach you are about to take makes sense or not is to know most (preferably all) of the pecularities of the CPU you are using. Once you do, a few things immediately becomes obvious.

Counters should be implemented as words, since the increment and decrement instructions work with words only, not bytes.

Counting a number of occurrences (looping or whatever) is best done by counting down, as the status bits are there to tell you when you reach the end of the count if, and only if, that's zero. If you count to some other value, you need an explicit compare to the end value. With DEC, compare with zero is implicit. As has been shown above, by clever use of the different status flags, you can detect either reached zero or passed zero when counting down.

 

Another special function of the TMS 9900 is the workspace registers, and the related context switch subroutine call (BLWP). When you return from such a call, by RTWP, the CPU will restore the context you have by reloading WP, PC and ST registers from R13-R15. This means that if you have a complex test sequence, preferably that's used in more than one place in your program, it may be an advantage to call it using BLWP instead of BL. Inside the BLWP subroutine, the last thing you do is to set the status bits as a result of your test in R15, then RTWP. Now if you use the EQual bit to indicate the result, you can immediately do a JEQ or JNE after calling the routine. You can use all status bits as you like, as long as you know what you want them to mean. Thus your test routine can come up with multiple results, which you test and handle by a bunch of jump instructions, checking equal, overflow, carry or whatever you like.

Especially since the TMS 9900 doesn't have the LST instruction (Load STatus) found in the TMS 9995, this is a handy concept. BLWP calls take some time, but we all know by now that the best thing on the TMS 9900 is to execute fewer instructions. Almost always is it better to use a slower but more complete instruction than using two simple ones.

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

On 9/2/2022 at 3:01 PM, Sergioz82 said:

In the manuals I use (Compute!'s and Fundamentals of TI-99/4A Assembly Language) it's not really specified it's a word instruction. They just say it decreases a memory location.

Yeah, the 9900 actually has comparably weak byte support, even the hardware can only access 16-bit words at a time (it can not access a byte! It reads a word, modifies the one byte, and writes the whole word back). So unless it explicitly says BYTE, then assume 16-bit word.

 

  • Like 2
Link to comment
Share on other sites

True. The Editor/Assembler manual just states that DEC decrements the source operand. The manual does as @Tursi recommends above, assumes 16-bits for instructions with only one variant. They specify if there are alternatives. Like for the instructions A and AB, they specifically write operand (byte) for AB and operand (word) for A.

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Hello, the development is going on. Everything moves as it should, I also added some special items (the idea is to make an unusual Pacman). I hope I can post a video soon after some polishing.

Unfortunately, I ran into a pesky problem: whenever I push the fire button (or press any key) the sprites revert back to 1X magnification.

 

I read on Compute!'s manual that I have to store E1 @>83DA to avoid losing the setting after KSCAN. and that's what I do, I copied the example and it's the only point where I manipulate 83DA.

It's also outside the game loop, it's executed  only once at startup.

 

*SPRITES 2X                                                                     
       LI R0,>01E1                                                              
       BLWP @VWTR                                                               
       SWPB R0                                                                  
       MOVB R0,@>83DA     

 

Problem is, E1 @>83DA gets replaced with A0 after some game loops. As long as I move the joystick magnification remains 2X. The moment I press fire (the Q key actually but it happens with any key) sprites pop back to 1X

 

The debugger says it's reset at LIMI 2 instruction. 

What am I doing wrong? 

 

in the loop sequence it's

 

LP     LIMI 2                                                                   
       LIMI 0                                                                   
                                                                                
       BLWP @MOVPAC * KSCAN is called inside MOVPAC                                                            
       BLWP @MOVFAN                                                             
       BLWP @MOVSCA                                                             
       BL @UPDTIM                                                               
                                                                                
       BLWP @UPDVDP  *This routine copies the sprite attribute list in RAM to VDP. It also waits for the end of screen drawing                                                
                                                                                
       JMP LP              

Edited by Sergioz82
Posted by mistake before completing the description
  • Like 1
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...