Jump to content

Photo

Trying to learn TI programming

TI-99/4A Editor/Assembler Assembly Extended BASIC Mini Memory

76 replies to this topic

#1 Pheonix ONLINE  

Pheonix

    Space Invader

  • 30 posts

Posted Mon May 15, 2017 5:31 PM

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 by Pheonix, Mon May 15, 2017 5:32 PM.


#2 adamantyr OFFLINE  

adamantyr

    Stargunner

  • 1,044 posts

Posted Mon May 15, 2017 5:54 PM

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?



#3 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Mon May 15, 2017 6:01 PM

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



#4 senior_falcon OFFLINE  

senior_falcon

    Dragonstomper

  • 828 posts
  • Location:Lansing, NY, USA

Posted Mon May 15, 2017 7:15 PM

 

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.


  • RXB likes this

#5 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Mon May 15, 2017 8:36 PM

Sorry, still not working :(



#6 senior_falcon OFFLINE  

senior_falcon

    Dragonstomper

  • 828 posts
  • Location:Lansing, NY, USA

Posted Mon May 15, 2017 9:29 PM

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 by senior_falcon, Mon May 15, 2017 9:57 PM.


#7 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Tue May 16, 2017 12:16 AM

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


#8 senior_falcon OFFLINE  

senior_falcon

    Dragonstomper

  • 828 posts
  • Location:Lansing, NY, USA

Posted Tue May 16, 2017 5:55 AM

 

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.



#9 senior_falcon OFFLINE  

senior_falcon

    Dragonstomper

  • 828 posts
  • Location:Lansing, NY, USA

Posted Tue May 16, 2017 1:26 PM



       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



#10 digdugnate ONLINE  

digdugnate

    Chopper Commander

  • 227 posts
  • Location:SW Missouri

Posted Tue May 16, 2017 2:36 PM

I'm learning Assembly, too- this is a cool thread!  Carry on!



#11 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Tue May 16, 2017 3:37 PM

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
 

Attached Files



#12 senior_falcon OFFLINE  

senior_falcon

    Dragonstomper

  • 828 posts
  • Location:Lansing, NY, USA

Posted Tue May 16, 2017 8:47 PM

You are making very good progress!  There are a couple places you could streamline your code:

LI R2,>FF00
MOVB R2
,@BUFFER
LI R2,BUFFER
BLWP
@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,R0
LI 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,8)  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.



#13 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Tue May 16, 2017 11:22 PM

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 by Pheonix, Tue May 16, 2017 11:38 PM.


#14 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Tue May 16, 2017 11:46 PM

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?



#15 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Wed May 17, 2017 4:08 AM

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 :(



#16 mizapf OFFLINE  

mizapf

    River Patroller

  • 2,308 posts
  • Location:Germany

Posted Wed May 17, 2017 5:23 AM

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.



#17 senior_falcon OFFLINE  

senior_falcon

    Dragonstomper

  • 828 posts
  • Location:Lansing, NY, USA

Posted Wed May 17, 2017 5:27 AM

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)



#18 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Wed May 17, 2017 7:38 AM

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.



#19 mizapf OFFLINE  

mizapf

    River Patroller

  • 2,308 posts
  • Location:Germany

Posted Wed May 17, 2017 8:29 AM

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 by mizapf, Wed May 17, 2017 8:36 AM.


#20 mizapf OFFLINE  

mizapf

    River Patroller

  • 2,308 posts
  • Location:Germany

Posted Wed May 17, 2017 10:03 AM

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.



#21 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Wed May 17, 2017 10:50 AM

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.



#22 adamantyr OFFLINE  

adamantyr

    Stargunner

  • 1,044 posts

Posted Wed May 17, 2017 11:27 AM

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.



#23 mizapf OFFLINE  

mizapf

    River Patroller

  • 2,308 posts
  • Location:Germany

Posted Wed May 17, 2017 11:43 AM

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.



#24 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Wed May 17, 2017 2:17 PM

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



#25 Pheonix ONLINE  

Pheonix

    Space Invader

  • Topic Starter
  • 30 posts

Posted Thu May 18, 2017 3:34 PM

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
 

Attached Files







Also tagged with one or more of these keywords: TI-99/4A, Editor/Assembler, Assembly, Extended BASIC, Mini Memory

2 user(s) are browsing this forum

2 members, 0 guests, 0 anonymous users