Pheonix Posted May 15, 2017 Share Posted May 15, 2017 (edited) First, I have a Mini Memory, but after fighting with it for a week, I cannot seem to work out exactly how to create something that will work with Extended Basic & a 32K RAM expansion. So, I've been looking to buy the Editor Assembler package (cart, disks, manual, ref, & keyboard overlay/slip.) Though that might take a while. Until then, I've been working with several emulators to try and get some practice in. The problem is that I cannot seem to write anything that will work. I tried entering a program from the "Introduction to Assembly Language for the TI Home Computer" book available elsewhere here, but it still wouldn't run. I tried the code exactly as written in the TI Basic option of the Editor/Assembler cartridge. I also tried adding in EQU instead of REF for the 2 calls (VSBW & VMBW,) in Extended BASIC. The example program ran fine with (3 Load and Run) from E/A (with the return altered by decrementing twice to freeze at the end.) Am I missing some important step? My code included... It's just a simple program to place text at a specific screen location. IDT 'HSTR' * MAY NOT BE NECESSARY, BUT DOESN'T HURT DEF HSTR VMBW EQU >2024 XMLLNK EQU >2018 NUMREF EQU >2000 *ALSO TRIED REMOVING ALL THESE AND USING STRREF EQU >2014 * COPY "DSK2.XB-EQUATES GPLWS EQU >83E0 *WHICH I PAINSTAKINGLY COPIED FROM THE STATUS EQU >837C *EDITOR/ASSEMBLER MANUAL (WITH CORRECTIONS) CFI EQU >12B8 *ALSO TRIED VALUES FROM A COMPUTE! BOOK FAC EQU >834A USRWS BSS 32 BUFFER BSS 256 SAVRTN DATA >0000 EVEN HSTR MOV R11,@SAVRTN LWPI USRWS CLR R0 LI R1,1 BLWP @NUMREF BLWP @XMLLNK DATA CFI DEC @FAC LI R3,32 MPY @FAC,R3 INC R1 BLWP @NUMREF BLWP @XMLLNK DATA CFI DEC @FAC A @FAC,R3 INC R1 LI R2,>FF00 MOVB R2,@BUFFER LI R2,BUFFER BLWP @STRREF MOV R3,R0 MOV R2,R1 MOVB *R1+,R2 SRL R2,8 BLWP @VMBW LWPI GPLWS MOV @SAVRTN,R11 CLR @STATUS B *R11 * ALSO TRIED RT END Edited May 15, 2017 by Pheonix 1 Quote Link to comment Share on other sites More sharing options...
+adamantyr Posted May 15, 2017 Share Posted May 15, 2017 Are you trying to write this in Mini Memory's line by line assembler or the Editor/Assembler? Slightly confused on that point... In E/A, you'll want to use REF, the subroutines don't exist otherwise. What page in the E/A manual is the example from? Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 16, 2017 Author Share Posted May 16, 2017 This code I'm trying to compile in E/A. I don't use REF because Extended BASIC doesn't support it. So I used the Extended BASIC equates listed on pages 415-418 of the E/A manual (with the corrections listed in the addendum.) The example (I didn't list it here,) that I also tried was in "Introduction to Assembly Language on the TI Home Computer," pages 25 & 26. Both my code, and the example compile fine. The example also runs fine in E/A's Load and Run option. However, it will not run in TI BASIC (with E/A cartridge installed,) or Extended BASIC (with the REF replaced with the Extended BASIC equates.) Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted May 16, 2017 Share Posted May 16, 2017 First, I have a Mini Memory, but after fighting with it for a week, I cannot seem to work out exactly how to create something that will work with Extended Basic & a 32K RAM expansion. So, I've been looking to buy the Editor Assembler package (cart, disks, manual, ref, & keyboard overlay/slip.) Though that might take a while. Until then, I've been working with several emulators to try and get some practice in. The problem is that I cannot seem to write anything that will work. I tried entering a program from the "Introduction to Assembly Language for the TI Home Computer" book available elsewhere here, but it still wouldn't run. I tried the code exactly as written in the TI Basic option of the Editor/Assembler cartridge. I also tried adding in EQU instead of REF for the 2 calls (VSBW & VMBW,) in Extended BASIC. The example program ran fine with (3 Load and Run) from E/A (with the return altered by decrementing twice to freeze at the end.) Am I missing some important step? My code included... It's just a simple program to place text at a specific screen location. IDT 'HSTR' * MAY NOT BE NECESSARY, BUT DOESN'T HURT DEF HSTR VMBW EQU >2024 XMLLNK EQU >2018 NUMREF EQU >2000 *ALSO TRIED REMOVING ALL THESE AND USING STRREF EQU >2014 * COPY "DSK2.XB-EQUATES GPLWS EQU >83E0 *WHICH I PAINSTAKINGLY COPIED FROM THE STATUS EQU >837C *EDITOR/ASSEMBLER MANUAL (WITH CORRECTIONS) CFI EQU >12B8 *ALSO TRIED VALUES FROM A COMPUTE! BOOK FAC EQU >834A USRWS BSS 32 BUFFER BSS 256 SAVRTN DATA >0000 EVEN HSTR MOV R11,@SAVRTN LWPI USRWS CLR R0 LI R1,1 BLWP @NUMREF BLWP @XMLLNK DATA CFI DEC @FAC LI R3,32 MPY @FAC,R3 INC R1 BLWP @NUMREF BLWP @XMLLNK DATA CFI DEC @FAC A @FAC,R3 INC R1 LI R2,>FF00 MOVB R2,@BUFFER LI R2,BUFFER BLWP @STRREF MOV R3,R0 MOV R2,R1 MOVB *R1+,R2 SRL R2,8 BLWP @VMBW LWPI GPLWS MOV @SAVRTN,R11 CLR @STATUS B *R11 * ALSO TRIED RT END There are two minor things wrong. First, you should return to XB this way: LWPI GPLWS B @>006A And there is no need for this line: MOV R11,@SAVRTN Second, the VMBW does not take into account the >60 screen offset in BASIC and XB. Something like this should do the trick: MOV R3,R0 address to start printing on screen MOVB *R2+,R3 length byte into R3 SRL R2,8 length is in least significant byte PRLP MOVB *R2+,R1 character to display in MSB of R1 AI R1,>6000 add the screen offset BLWP @VSBW write the byte INC R0 next screen position over one space DEC R3 decrement the counter JNE PRLP loop till finished I didn't test the above but it should work. There are better ways to handle the screen offset and some other places where your code can be streamlined, but this should get you started. 1 Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 16, 2017 Author Share Posted May 16, 2017 Sorry, still not working Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted May 16, 2017 Share Posted May 16, 2017 (edited) This works now. You had the wrong equate for NUMREF - it should be >200C. I had a mistake too; I wrote R2 when I meant R3. CALL LINK("HSTR",ROW,COL,STRING) IDT 'HSTR' * MAY NOT BE NECESSARY, BUT DOESN'T HURT DEF HSTR,TEST VSBW EQU >2020 VMBW EQU >2024 XMLLNK EQU >2018 NUMREF EQU >200C *ALSO TRIED REMOVING ALL THESE AND USING STRREF EQU >2014 * COPY "DSK2.XB-EQUATES GPLWS EQU >83E0 *WHICH I PAINSTAKINGLY COPIED FROM THE STATUS EQU >837C *EDITOR/ASSEMBLER MANUAL (WITH CORRECTIONS) CFI EQU >12B8 *ALSO TRIED VALUES FROM A COMPUTE! BOOK FAC EQU >834A USRWS BSS 32 BUFFER BSS 256 HSTR LWPI USRWS very important to set your own workspace when using XB. Do this first thing! CLR R0 LI R1,1 BLWP @NUMREF get row BLWP @XMLLNK DATA CFI now row is integer TEST MOV @FAC,R3 row to R3 DEC R3 have to decrement SLA R3,5 mpy by 32 INC R1 BLWP @NUMREF get column BLWP @XMLLNK DATA CFI now column is integer A @FAC,R3 add column to row DEC R3 INC R1 LI R2,>FF00 MOVB R2,@BUFFER LI R2,BUFFER BLWP @STRREF MOV R3,R0 screen address into R0 MOVB *R2+,R3 length byte to MSB of R3 SRL R3,8 now length is in LSB of R3 PRLP MOVB *R2+,R1 Character to display on the screen AI R1,>6000 add the screen offset BLWP @VSBW and write it INC R0 1 space to right on screen DEC R3 done with loop? JNE PRLP nope, keep looping LWPI GPLWS back to XB B @>006E "" END Edited May 16, 2017 by senior_falcon 1 Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 16, 2017 Author Share Posted May 16, 2017 OK, I missed that. I could have sworn I checked the numbers with the Addendum. Also, is the return "B @>006A" or "B @>006e"? I'm sure hoping it was a typo and isn't something that changes. All that being said, I'm now starting to wonder why VMBW doesn't work (and, yes, I did run a loop to add >60 to every character.) Not that it makes that much of a difference, I guess. On other systems I've programmed on, making ROM (or Kernel etc...) calls actually slow things down a bit. So, making a single call instead of multiple calls would be preferable (usually.) May not make a big difference, but these are learning projects. I do them because a they caught my fancy, and to learn. The only advantage this little program has, really, is the ability to display outside the print/display range (2 rows on each side of the screen.) It can be done with a CALL HCHAR loop, but that's slow. This is much faster Anyways, the different code (only posting what is different from the working version.) INC R1 LI R2,>FF00 MOVB R2,@BUFFER LI R2,BUFFER BLWP @STRREF BL @ADJUST MOV R4,R0 LI R1,BUFFER MOVB *R1+,R2 SRL R2,8 BLWP @VMBW LWPI GPLWS B @RTNPTR ADJUST MOVB *R2+,R1 LOOP1 MOVB *R2,R3 AI R3,>6000 MOVB R3,*R2+ DEC R1 JNE LOOP1 B *R11 END Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted May 16, 2017 Share Posted May 16, 2017 OK, I missed that. I could have sworn I checked the numbers with the Addendum. Also, is the return "B @>006A" or "B @>006e"? I'm sure hoping it was a typo and isn't something that changes. All that being said, I'm now starting to wonder why VMBW doesn't work (and, yes, I did run a loop to add >60 to every character.) Not that it makes that much of a difference, I guess. On other systems I've programmed on, making ROM (or Kernel etc...) calls actually slow things down a bit. So, making a single call instead of multiple calls would be preferable (usually.) May not make a big difference, but these are learning projects. I do them because a they caught my fancy, and to learn. The only advantage this little program has, really, is the ability to display outside the print/display range (2 rows on each side of the screen.) It can be done with a CALL HCHAR loop, but that's slow. This is much faster Oops - it should indeed be B @>006A. It only changes when it gets close to midnight :-) I will edit the post. The screen offset of >60 or 96 is there to maximize the space in VDP ram that can be used for a BASIC or XB program. You can see the screen offset with Classic99's debugger (Edit>Debugger and choose VDP.) The screen is from >0000 to >02FF and you'll see it is filled with the character >80. If you do CALL GCHAR(5,5,G) XB tells you that G is 32 (or >20) even though you can see that it is >80. VMBW does not take that into account. For some projects I have used a VMBW60 which works like the normal VMBW but adds the offset. Another way of dealing with it would be to have a loop that adds >60 to each byte of the string. Then VMBW will be usable as is. Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted May 16, 2017 Share Posted May 16, 2017 INC R1 LI R2,>FF00 MOVB R2,@BUFFER LI R2,BUFFER BLWP @STRREF BL @ADJUST MOV R4,R0 LI R1,BUFFER MOVB *R1+,R2 SRL R2,8 BLWP @VMBW LWPI GPLWS B @RTNPTR ADJUST MOVB *R2+,R1 LOOP1 MOVB *R2,R3 AI R3,>6000 MOVB R3,*R2+ DEC R1 JNE LOOP1 B *R11 END One of the neat things about the 9900 is that you don't have do do all the operations in a register. For example, in LOOP1 you could do it this way: ADJUST MOVB *R2+,R1 SRL R1,8 !don't forget to put into LSB LOOP1 AB @HX6000,*R2+ DEC R1 JNE LOOP1 HX6000 DATA >6000 1 Quote Link to comment Share on other sites More sharing options...
digdugnate Posted May 16, 2017 Share Posted May 16, 2017 I'm learning Assembly, too- this is a cool thread! Carry on! Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 16, 2017 Author Share Posted May 16, 2017 That shortens the code a bit You also pointed out a mistake that I seem to be unable to keep from happening I seem to be consistently forgetting that SRL line. VMBW works now. Though, I found out there is a way to avoid using ROM routines for it all together. Found it a couple of days ago, actually, but just saved it for later. When learning, I like to finish a problem before moving on to the next So, a cleaner (from what I understand,) method would be: MOV R3,R0 LI R1,BUFFER MOVB *R1+,R2 SRL R2,8 ORI R0,>4000 ANDI R0,>7FFF * Original code doesn't have this, but starting 2 bits must be 01 SWPB R0 * The address register expects Low Byte first MOVB R0,@VDPWA SWPB R0 * Now for the Hi Byte MOVB R0,@VDPWA * Start address now set. NOP * Says this is required. LOOP1 MOVB *R1+,R3 AI R3,>6000 MOVB R3,@VDPWD DEC R2 JNE LOOP1 * Much larger loop, does not using a ROM Routine make up the difference? So I now have 3 working solutions to the original problem I posed myself. I have to admit that, until recently, the only assembly I ever knew or used was for the 6502. For everything else, I used C+. Not counting BASIC, of course. BASIC is all I ever used on the TI until recently. I had a Mini Memory back in the day as well, but never got into it that much. Also added in code to check the X & Y values given. X must be from 1 to 32 and Y from 1 to 24. Any numbers outside that range now cause a "Bad Value" error. The size of the buffer gave me pause, until I found out that strings have a limit of 255 characters. This also applies to stings passed as a parameter. So, unless there is a mod I don't know about, a program won't be able to pass more than 255 characters at a time to this. So, one problem solved. Completely solved as far as I can determine. I've also expanded my understanding a bit Which was the entire purpose of this project. I've also attached my XB-EQUATES file for anyone that wants it. I did a search prior to typing it it myself, but couldn't find it. I had removed the COPY line and added in the equates earlier so people could see what I was using. It will need to be converted for use on a TI-99/4A, as this is in PC format. The final code: IDT 'HSTR' DEF HSTR COPY "DSK2.XB-EQUATES" USRWS BSS 32 BUFFER BSS 256 EVEN HSTR LWPI USRWS CLR R0 LI R1,1 BLWP @NUMREF BLWP @XMLLNK DATA CFI MOV @FAC,R3 DEC R3 CI R3,>17 JH BADVAL SLA R3,5 INC R1 BLWP @NUMREF BLWP @XMLLNK DATA CFI MOV @FAC,R2 DEC R2 CI R2,>1F JH BADVAL A R2,R3 INC R1 LI R2,>FF00 MOVB R2,@BUFFER LI R2,BUFFER BLWP @STRREF MOV R3,R0 LI R1,BUFFER MOVB *R1+,R2 SRL R2,8 ORI R0,>4000 ANDI R0,>7FFF SWPB R0 MOVB R0,@VDPWA SWPB R0 MOVB R0,@VDPWA NOP LOOP1 MOVB *R1+,R3 AI R3,>6000 MOVB R3,@VDPWD DEC R2 JNE LOOP1 LWPI GPLWS CLR @STATUS B @RTNPTR BADVAL LI R0,ERRBV BLWP @ERR END XB-EQUATES.txt Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted May 17, 2017 Share Posted May 17, 2017 You are making very good progress! There are a couple places you could streamline your code: LI R2,>FF00MOVB R2,@BUFFERLI R2,BUFFERBLWP @STRREF can become: LI R2,BUFFER SETO *R2 This sets the max length at >FF. The next byte is also >FF, but that won't hurt anything. BLWP @STRREF MOV R3,R0LI R1,BUFFER Here you can just MOV R2,R1 as I think R2 is unmodified by STRREF. (one word instruction instead of 2) ORI R0,>4000 ANDI R0,>7FFF Here I usually just AI R0,>4000 and the 1st 2 bits will be 01. The highest address in VDP ram is >3FFF and your limit checks will keep it from being bigger. ORI would work too. The NOP is not needed. It's just to kill some time, which the next instruction does anyway. Now here's a couple things to consider: 1 - When you CALL LINK the address >8312 has the number of arguments passed. If, for example, you had in XB: CALL LINK("HSTR",R,C,A$,R1,C1,B$) then >8312 would have a 6. Your assembly subroutine can move >8312 to a register (then don't forget to SRL Rx, After you print A$ at R and C, subtract 3 from the register, check for zero. If not zero then loop back to beginning to get the next row,column and string to print. You can pass up to 16 numbers and strings, so you can display up to 5 strings this way. 2 - You can have a 4th number that is passed after the string. This number is used by the a/l subroutine to add to the screen position. For example: CALL LINK("HSTR",R,C,A$,1) would print each character 1 space to the right. CALL LINK('HSTR",R,C,A$,32) would add 32 to the screen position and would print text vertically. This way you can print text in any direction - backwards, upward, diagonally, etc. (Thanks to Willsy for this idea.) 3 - You could have CALL LINK("HSTR") and CALL LINK("VSTR") The first instruction of HSTR could be LI R8,1 and then a jump to the code to display text. VSTR would have LI R8,32 as the first instruction. Then the code to display the string would add R8 to the screen position for each character. You would need limit checks to keep from going off the bottom or top of the screen. Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 17, 2017 Author Share Posted May 17, 2017 (edited) Since I'm currently working on streamlining, this actually helps a lot I had also been looking into counting parameters as well, just found the >8312 address when I got the e-mail notification of a reply. I was also considering adding in another parameter for direction. Though I was actually considering 8. Allowing diagonal printing as well. One issue with all of it, that I need to take into account with the code above as well. By doing things manually like this, I could without much difficulty, end up printing past the video RAM. So, assuming I add in direction, Check the new value of R0 after INC/DEC. If negative, add 768. If over 767 subtract 768. Instead of worrying about which direction is printing, just check the carry bit (0 if result is negative,) and add 768 if it's not set. So, . .Code that adjusts R0 as needed. R4 will be set to 768 . JOC ISPOS A R4,R0 ISPOS C R0,R4 JL ISGOOD S R4,R0 ISGOOD DEC R2 . .rest of code . The 2 routines (ADD & SUB 768,) would return to just past the checks. Would mean moving the adjustment away from auto increment (*R0+). But that would be needed to add in direction anyway. Also about to look into other possibilities as well. Would be nice if I could set something like reverse text, or some such. Can't do it in basic, but would be nice Adding in direction options will come next, though. After I finish streamlining and adding in a location safety check. EDIT: Forgot this bit, sorry... Also planning to make everything past the text string optional. Problem with that... Would limit it to 1 per call. If I find enough options, though, that wouldn't be that much of an issue. Something like "CALL LINK("PSTR",Y,X,TEXT$[,DIRECTION,SPACING,ETC...)". Yes, I've renamed it. Originally it was based on HCHAR & VCHAR, so HSTR with maybe a VSTR later. But if I'm going to expand to (V)ertical, thought I might add in other options as well. Now it stands for (P)ut (STR)ing. EDIT2: Great... was looking at my old code that used VSBW. Looking at the current code, it probably doesn't matter if it would be printing past the screen. VDP probably does that itself. (I can hope.) Won't have to worry about that until I add in direction, and will need to call the position out every time. Edited May 17, 2017 by Pheonix Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 17, 2017 Author Share Posted May 17, 2017 Separate post for this. One, because I've already edited twice. Two, because it is a minor subject change from my last post. Does CALL LINK allow empty parameters? Such as "CALL LINK("PSTR",15,1,"HELLO",,C)". In the example, everything past "HELLO" is optional, with default values for the rest. But someone wants to add in option 'C' (whatever that is,) but just wants to use the default for the previous option. Will CALL LINK allow it? Will it just put 0 in for the missing parameter, or will I have to handle that myself? Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 17, 2017 Author Share Posted May 17, 2017 Well, after a false start, I now have directional control working. Still debating just setting a direction (forward, backward, up, down, the 4 diagonals,) and setting a +/- spacing (32 for down, 33 for down/left, etc...) That is neither here nor there right now, it's a decision I'll eventually make. This problem exist regardless of that decision (I've tried it both ways.) The code works for when the position falls below 0, but if it goes over 767, it doesn't. I've studied it, read the manual, tried several methods, I just cannot get it to work. The code: A R5,R0 JOC ISPOS A R6,R0 ISPOS C R0,R6 JL ISGOOD S R6,R0 ISGOOD DEC R2 JNE LOOP1 R5 has the calculated or given spacing. R6 is set to 768, though I've also used @DEC768 (DATA 768). In both cases, the first part works, moving from the top left to the bottom right. It's the secon part that doesn't. "C R0,R6" is supposed to set he >L flag if the value in R0 is greater than that in R6 (logical, so -1 to -32768 are all "greater" than 32767.) If the values are equal, it sets the E flag. JL, will check both the >L and the E flag and jump if they are both unset. So, if adding to R0 made it 768 (for example,) C would set the equal flag, JL would then "not" jump, and "S R6,R0" would then subtract R6 from R0, putting the result (768 - 768 = 0) into R0 (which is the desired goal here.) Only, it doesn't appear to do so. I don't know what the numbers are changing to (what I wouldn't give for a "good" debugger,) but they aren't changing to what I need them to. Either the test is failing, or the subtraction. I did get it to work with removing the over 767 check & subtraction. Replacing them with just a divide by 768. Not happy with that solution as the action is taken every time (even when not needed.) It also ties up another register. Aesthetics only, I know, but still... The original method "should" work, unless I'm missing something Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 17, 2017 Share Posted May 17, 2017 When R0 is 760 and R5 is 32, adding both will yield 792, which does not create a carry. So you add another 768, that is 1560. This is logically higher than 768, so you subtract R6 again, which is 792. I guess you wanted to get 24. Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted May 17, 2017 Share Posted May 17, 2017 There is a nice debugger in Classic99. You can set breakpoints, but how do you know where? Simple - just have a label such as BP where you want to break the program and DEF BP at the beginning. Load the assembly code and look at CPU ram at >3F00 and you should see "BP" in the table. The last two bytes on that row are the address of BP. In the upper right of the debugger screen you can set the breakpoint. For example, *1234 and "add breakpoint" will break whenever >1234 if written to or accessed. (Use the ? to find out the options) Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 17, 2017 Author Share Posted May 17, 2017 When R0 is 760 and R5 is 32, adding both will yield 792, which does not create a carry. So you add another 768, that is 1560. This is logically higher than 768, so you subtract R6 again, which is 792. I guess you wanted to get 24. I actually miss the V register Echoes the MSb of the result. Then I can jump if not negative. Just re-read the pages in question, and all it says is that if there is a carry of bit 0 the carry status bit is set. It also says that the overflow bit is set if there is an overflow. Thinking on it, it seems to say that the carry bit & overflow bit are serving the same purpose here. Bit 0 producing a carry is the same thing as an overflow, isn't it? However, to make sure, I've tried expanding it to a jump if arithmetically greater than (1 to 32767,) and then a separate jump if equal (0). Same result. Going below 0 works fine, going over 767 doesn't. I'm starting to think it would be more efficient (and easier,) to just add 768, then divide by 768 every time. No checks. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 17, 2017 Share Posted May 17, 2017 (edited) No, carry and overflow are different and occur at different places in the set of numbers. I know that carry and overflow are sometimes hard to understand. Remember that the CPU is always adding, even when doing subtractions. The signed or unsigned nature is something that is considered by the jump operation, not by the adder. When you add two numbers, and your result gets beyond FFFF, you get a carry. Read again: When you add two signed numbers, and your result gets higher than -1, you get a carry. Carry is set when there is a carry of 1 when adding the leftmost bit (0). Examples: A000 + 1000 = B000 (no carry) 7000 + 2000 = 9000 (no carry) F000 + 1000 = 0000 (carry) 0000 + FFFF = FFFF (no carry) 0001 + FFFF = 0000 (carry) 0002 + FFFF = 0001 (carry) So when you do a DEC on a register, for instance, you actually add FFFF. This means that the carry bit will be set whenever the value is not 0, because in that case, the result is FFFF with no carry (again, try to forget the signed arithmetics). For that reason you can do a loop like DEC R1 JOC $-2 which counts down and leaves the loop with R1=FFFF. Now for overflow. Overflow is set when the sum becomes negative for two positive numbers, or the sum becomes positive for two negative numbers. This means the relevant point is 8000. A000 + 1000 = B000 (no overflow) 7000 + 2000 = 9000 (overflow, because both operands are positive, and the result is negative) F000 + 1000 = 0000 (no overflow, because the operands have different signs) 0000 + FFFF = FFFF (no overflow, as above) 0001 + FFFF = 0000 (no overflow, as above) 0002 + FFFF = 0001 (no overflow, as above) 8000 + FFFF = 7FFF (overflow) Edited May 17, 2017 by mizapf 3 Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 17, 2017 Share Posted May 17, 2017 One more thing, which really puzzled me, and which I first did wrong in the TMS9900 emulation in MAME, is the following observation: S R0,R1 When both R0 and R1 are zero, carry is set. Why? - Because subtraction is obviously not implemented as adding the two's complement of the first operand (which would still be 0), but as adding its one's complement (FFFF) and then incrementing the result by one. Thus, you get 0000 - 0000 = 0000 + FFFF + 1 = 0, and carry is set. Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 17, 2017 Author Share Posted May 17, 2017 Thanks for the clarification. I sort of knew that, but I'm still struggling a bit at grasping signed numbers when programming. I tend to think "unsigned" when programming. A byte ranges from 0 to 255, not -128 to 127. I'm used to rarely dealing with signed numbers, and when I absolutely had to, having a status bit that told me what was what. The TI doesn't have that bit. When I first read the manual on the A (to try and determine if there was a way to see if the result was negative,) it seemed to read that if the result was positive or zero, the carry bit would be set. Reading it again, I've realized my mistake there. I'm used to only using the carry flag for multi word addition/subtraction and for controlling/detecting bits when doing a bit shift. SLC/SRC (didn't have the option of shifting more than one bit at a time,) would use the state of the carry flag to determine what was rolled into the byte, and the bit rolled off would determine the new state of the carry flag. Easy to get the remainder with 2's complement division with that. Quote Link to comment Share on other sites More sharing options...
+adamantyr Posted May 17, 2017 Share Posted May 17, 2017 A paradigm when working with values in assembly is, the context in which you use them determines their value. There's no such thing as a "signed byte" or an "unsigned byte" in the sense that it's one or the other. It's how you TREAT it that matters. A case in point: A lot of the times when I am doing a boundary check for XY coordinate systems, If they run 0-N, I can actually just check that it's "Low or equal" to N and it actually will check for N+ as well as negative values. Quote Link to comment Share on other sites More sharing options...
+mizapf Posted May 17, 2017 Share Posted May 17, 2017 Correct. The interesting thing about two's complement is that the unsigned result of an addition of unsigned values is also the signed result if those values were understood as signed: 0xFFF0 + 0xFFF5 = 0x1FFE5 = 0xFFE5 (for 16 bit) And 0xFFF0 = -0x0010, and 0xFFF5 = -0x000B, and you guessed, 0xFFE5 = -0x001B So the CPU calculates a single result and sets the status flags L>, A>, EQ, C, and OV for both cases. The programmer decides by checking these flags which interpretation to use. However, this does not work for multiply and divide, so the commands MPY and DIV assume unsigned values, and the TMS9995 add the commands MPYS and DIVS for signed arithmetic. Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 17, 2017 Author Share Posted May 17, 2017 Still learning TI, another alternative would have been the COC, thought that would have required using a memory address (doesn't have an immediate mode.) On the surface, all a compare is, is a subtraction. Subtract the source from the destination and use the result to set the appropriate flags, then discard the results. Also, adding and subtracting involve the same actions, with the only difference being the carry flow. If the bits are the same, result is a zero, if they are different it's a 1 (so XOR each bit in turn.) Now, On an addition, if the result is a 0 and the source is a 1, then carry is triggered. On a subtraction, if the result is a 1 and the destination is a 0, a carry (borrow) is triggered. Move to the next bit. If carry is set, XOR the bit with 1 first. On subtraction, if the result is one, set the carry again. On addition, set it if the result is 0. Then do the same procedure as the first bit. Before anyone comments, it's not possible to double trigger carry in this case. This continues until the final bit, and the final carry (borrow,) check. In addition, the carry is set if the cascade goes that far, and cleared if not. In subtraction its the other way around (clear if used, set if not.) On a 6502, overflow is only triggered if the result went over 255 (8 bit CPU.) There was no "underflow" detection other than the carry bit. The only recognition of signed values was that bit 0 (book calls it bit 8, opposite of the TI book, but that's sophistry,) of the result is mirrored in the S flag (bit 0 of the register.) I never worried about sign, when I wrote the code, I treated all variables as unsigned integers. Just using ADC or SBC as called for. The only times I had to worry about it were when dealing with what is displayed. In those cases, I depended on the carry bit more often than not. I have memories of using the S flag on numerous occasions, though, but don't completely remember the context (it has been 30 years.) Until now, this has worked just fine. Until I went looking for the S flag, because I needed a positive number less than 768 (0-768 for precision.. 0 is considered a positive number, because bit 0 is clear.) I was rather surprised that the TI didn't have one. So I went looking for an alternative that wouldn't require another compare. Doesn't appear to exist. Closest I can get is a JGT & JEQ. It turns out, that iteration of my code had a type I kept missing. Instead of adjusting R3 when it went over 767, I was adjusting R2 (the register I used when using the DIV Modulo method.) Quote Link to comment Share on other sites More sharing options...
Pheonix Posted May 18, 2017 Author Share Posted May 18, 2017 Well, this project seems to be finished. I can't find any more ways to optimize the code. But that doesn't mean there isn't any, I'm just not familiar enough with TI assembly yet. A quick skim of the op-code listing in the E/A manual & checking anything that sounded promising to me failed to turn up anything that didn't seem to actually make it worse. The completed code (along with a TI disk image) are below. Now starting my next project, not sure what it's going to be yet. I was looking into experimenting with disk access, but it seems the Manual doesn't want to list the equate values for DSRLNK & GPLLNK. Since the equate values listed in the Mini Memory modules differ from those in the E/A manual, I'm not sure if using those values would work or not. Until it actually works, I wouldn't know if the code was failing because of my mistakes or because the equate values are wrong.This is a learning thread, for me, so I'm not sure if I should just continue this thread with my next project or start a new one. Will give it some thought, and make a decision when the time comes IDT 'PSTR' DEF PSTR COPY "DSK2.XB-EQUATES" USRWS BSS 32 BUFFER BSS 256 SCRSZ DATA 768 EVEN PSTR LWPI USRWS LI R4[attachment=508342:Work.dsk],>0400 CB @NUMARG,R4 JH BADARG CLR R0 LI R1,1 BLWP @NUMREF BLWP @XMLLNK DATA CFI MOV @FAC,R3 DEC R3 CI R3,23 JH BADVAL SLA R3,5 INC R1 BLWP @NUMREF BLWP @XMLLNK DATA CFI MOV @FAC,R2 DEC R2 CI R2,>1F JH BADVAL A R2,R3 INC R1 LI R2,BUFFER SETO *R2 BLWP @STRREF LI R2,769 CB @NUMARG,R4 JL PUTSTR INC R1 BLWP @NUMREF BLWP @XMLLNK DATA CFI MOV @FAC,R2 MOV R2,R4 ABS R4 CI R4,>0180 JH BADVAL PUTSTR LI R1,BUFFER MOVB *R1+,R4 SRL R4,8 LOOP1 ORI R3,>4000 SWPB R3 MOVB R3,@VDPWA SWPB R3 MOVB R3,@VDPWA ANDI R3,>3FFF MOVB *R1+,R0 AI R0,>6000 MOVB R0,@VDPWD A R2,R3 JGT ISPOS A @SCRSZ,R3 ISPOS C R3,@SCRSZ JL ISGOOD S @SCRSZ,R3 ISGOOD DEC R4 JNE LOOP1 LWPI GPLWS CLR @STATUS RT BADVAL LI R0,ERRBV BLWP @ERR BADARG LI R0,ERRIAL BLWP @ERR END Work.dsk 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.