Willsy Posted August 31, 2022 Share Posted August 31, 2022 Apologies - I see I have more or less copied @apersson850's post - I should have read down-thread first! 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted August 31, 2022 Share Posted August 31, 2022 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? Quote Link to comment Share on other sites More sharing options...
+9640News Posted August 31, 2022 Share Posted August 31, 2022 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. 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted August 31, 2022 Share Posted August 31, 2022 4 hours ago, Willsy said: Apologies - I see I have more or less copied @apersson850's post - I should have read down-thread first! Reading your post, it seems we are two souls with one opinion! 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted August 31, 2022 Share Posted August 31, 2022 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. 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 31, 2022 Share Posted August 31, 2022 I never knew about BES. Somehow skipped past it in the manual. Thanks! 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted August 31, 2022 Share Posted August 31, 2022 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 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 31, 2022 Share Posted August 31, 2022 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. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 31, 2022 Share Posted August 31, 2022 I have used BES very seldom, but, believe it or not, I used it recently to create a small return stack for BL calls. ...lee 2 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted September 1, 2022 Author Share Posted September 1, 2022 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. Spacm.mp4 7 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 1, 2022 Share Posted September 1, 2022 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. 1 Quote Link to comment Share on other sites More sharing options...
GDMike Posted September 1, 2022 Share Posted September 1, 2022 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! Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted September 2, 2022 Author Share Posted September 2, 2022 (edited) 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 September 2, 2022 by Sergioz82 Correction in a code comment 1 Quote Link to comment Share on other sites More sharing options...
Willsy Posted September 2, 2022 Share Posted September 2, 2022 IIRC JGT is a signed comparison. Have a look at the JHE which is (IIRC) unsigned. I could have that the wrong way around! The Editor Assembler manual is your friend! 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted September 2, 2022 Author Share Posted September 2, 2022 @Willsy I checked, you're right. I made a quick test by replacing the conditional jumps but it's worse, the ghost don't chase. It's definitely something realated to signed/unsigned comparison though. Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted September 2, 2022 Author Share Posted September 2, 2022 @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.. 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 1 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted September 2, 2022 Share Posted September 2, 2022 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. 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 2, 2022 Share Posted September 2, 2022 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.. Anyway I replaced DEC with AB instruction and now it works. You can only use DEC on a word. 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 2, 2022 Share Posted September 2, 2022 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. Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted September 2, 2022 Author Share Posted September 2, 2022 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. 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted September 3, 2022 Share Posted September 3, 2022 (edited) 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 September 4, 2022 by apersson850 4 Quote Link to comment Share on other sites More sharing options...
Tursi Posted September 6, 2022 Share Posted September 6, 2022 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. 2 Quote Link to comment Share on other sites More sharing options...
apersson850 Posted September 7, 2022 Share Posted September 7, 2022 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. 1 Quote Link to comment Share on other sites More sharing options...
Sergioz82 Posted September 26, 2022 Author Share Posted September 26, 2022 (edited) 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 September 26, 2022 by Sergioz82 Posted by mistake before completing the description 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted September 26, 2022 Share Posted September 26, 2022 (edited) I think you need to store it in >83D4 and not >83DA. http://www.unige.ch/medecine/nouspikel/ti99/ints.htm#blank Edited September 26, 2022 by Asmusr 1 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.