Jump to content
IGNORED

Assembly code samples


GDMike

Recommended Posts

       AORG >CBD4           *DELETE
TEST   TEXT 'COMEFINDMEBRO' *DEL
       AORG >7D00           *DEL
QUERI  TEXT 'COMEFINDMEBRO' *SET THIS LINE UP IN YOUR PROGRAM, DELETE FROM HERE
       DATA 0               *THIS SHOULD FOLLOW YOUR BUFFER SPACE, DELETE FROM HERE
VALID  DATA 0
ENTRY  LI   R4,>A000        *SET THIS VALUE UP IN YOUR PROGRAM, DELETE FROM HERE
       LI   R6,>0000        *SET THIS VALUE UP IN YOUR PROGRAM, DELETE FROM HERE
SEARCH LI   R3,QUERI        *THIS IS THE LINE TO BL TO!
CMPARE CB   *R3,*R4+
       JEQ  NEXT
       C    R4,R6
       JNE  CMPARE
OUTNO  RT
NEXT   MOV  R4,R5
CONT   CB   *R3,@VALID
       JEQ  OUTYES
       C    R4,R6
       JEQ  OUTNO
       INC  R3
       CB   *R3,*R4+
       JEQ  CONT
RESTAR MOV  R5,R4
       JMP  SEARCH
OUTYES MOV  R5,R4
       DEC  R4
       RT
       END

Here, I removed the testing lines for you. I hope I did it right.

VALID  DATA 0
SEARCH LI   R3,QUERI        *THIS IS THE LINE TO BL TO!
CMPARE CB   *R3,*R4+
       JEQ  NEXT
       C    R4,R6
       JNE  CMPARE
OUTNO  RT
NEXT   MOV  R4,R5
CONT   CB   *R3,@VALID
       JEQ  OUTYES
       C    R4,R6
       JEQ  OUTNO
       INC  R3
       CB   *R3,*R4+
       JEQ  CONT
RESTAR MOV  R5,R4
       JMP  SEARCH
OUTYES MOV  R5,R4
       DEC  R4
       RT
       END


Set up a buffer labeled QUERI, make sure to fill it with all zeros, before overlaying your search term.

This buffer should have an extra 1 or 2 zeros after it's max. length! This will indicate the search completed successfully, when found by my program.

 

R4=Where to start searching from.
R6=Where to stop searching.

BL @SEARCH

 

This should find the first occurrence, before returning to your program.


R4 will be set to the starting address of the found term.

or

R4 will be equal to R6 if nothing was found.


 Adding the length of the search term to R4, then BL-ing back to SEARCH, should allow you to find the next occurrence.

 

Good luck, you might need it.

:)
 

  EDIT:

 

I hope these comments make more sense to you, than they do to me!:lol:

 

VALID  DATA 0               TERM FOUND, COMPARATOR.
SEARCH LI   R3,QUERI        R3=SEARCH TERM'S ADDRESS. *THIS IS THE ENTRY ADDRESS!
CMPARE CB   *R3,*R4+        COMPARE 1st LETTER OF SEARCH TERM, TO THE LETTERS IN THE SPECIFIED RANGE, ONE LETTER AT A TIME.
       JEQ  NEXT            DO THEY MATCH? YES! CHECK IF SECOND LETTERS MATCH. NO! CONTINUE.
       C    R4,R6           HAVE WE REACHED THE END OF THE SEARCH RANGE YET?
       JNE  CMPARE          NO! CHECK NEXT LETTER IN THE SPECIFIED RANGE. YES! CONTINUE.
OUTNO  RT                   BYE, NOTHING FOUND.
NEXT   MOV  R4,R5           SAVE THE POSITION OF THE NEXT POSSIBLE FIRST LETTER MATCH ...TO RESTART FROM IN CASE THE SECOND LETTER DOESN'T MATCH, OR TO INDICATE WHERE AN OCCURRENCE WAS FOUND LATER.
CONT   CB   *R3,@VALID      HAVE WE ALREADY MATCHED ALL CHARACTERS?
       JEQ  OUTYES          YES! MATCH FOUND! NO! CONTINUE.
       C    R4,R6           HAVE WE REACHED THE END OF THE SEARCH RANGE YET?
       JEQ  OUTNO           YES! EXIT(NO OCCURRENCE(S)) FOUND. NO! CONTINUE.
       INC  R3              ADVANCE TO THE NEXT LETTER OF THE SEARCH TERM.
       CB   *R3,*R4+        COMPARE ADDITIONAL LETTERS, ONE AT A TIME.
       JEQ  CONT            YES! CHECK 3RD AND UP, LETTERS. IF A LETTER DOESN'T MATCH, CONTINUE.
RESTAR MOV  R5,R4           RESTORE NEXT POSSIBLE FIRST LETTER MATCH ADDRESS.
       JMP  SEARCH          KEEP SEARCHING FOR NEXT FIRST LETTER MATCH.
OUTYES MOV  R5,R4           RETRIEVE POSITION OF THE PREVIOUS NEXT POSSIBLE FIRST LETTER MATCH.
       DEC  R4              SET R4 TO THE POSITION OF THE FIRST CHARACTER OF THE MATCHED TERM.
       RT                   OCCURRENCE FOUND
       END                  FEELS SO GOOD!

:)

Edited by HOME AUTOMATION
  • Like 1
Link to comment
Share on other sites

This will take me awhile to figure out. Then port it over. But it's an idea. 

I was getting close with mine but I don't know what I'm doing wrong, so since yours works, ill try to understand it.

You're like the shell answer guy, and quick too!

Ok, it's almost bed time and I still gotta clean up, chars all over the place. Ty. I'll start on this code tommorow and toss mine out.

Oh btw, I can't use any bl code, I'm already bl to this routine. How to work around that?

Thank you. So happy to see working code.

Edited by GDMike
  • Like 1
Link to comment
Share on other sites

6 hours ago, mizapf said:

As our machine language programs have direct hardware control, the VRAM address auto-increment should be used, of course. I sometimes wonder how things would like like when we had a full multitasking operating system (maybe for the Geneve 2020?). It would be reasonable to block all direct access to hardware, only offering a system call interface (which would be our XOPs). In that case, we would need lots of simple routines in the kernel, like filling VRAM space.

 

(I just had a short look at the TMS99000 manual; interestingly, there is no restriction on memory access in the user mode, so any memory-mapped device would be directly accessible. But at least some CRU address ranges are restricted to kernel mode, and a memory-mapped device could then be guarded by a CRU bit.)

My multi-tasker makes sure anything critical runs to completion before giving up the device.  If the task switch overhead is low, which it can be on the 9900, you can turn off interrupts when you enter a time critical routine and turn them on when you leave. BUT... you must make sure those routines that do this are fast and small.  In my case it's cooperative tasking so interrupts are not required.

The big problem can be disk I/O which was not written with this in mind so they make cooperative tasking clunky stealing time while waiting for the drive.

  • Like 1
Link to comment
Share on other sites

The VDP Single Byte Multiple Write (VSMW) is not part of the TI provided routines.  I don't know if I coined the term, but I've used it since around 2006 (or earlier) when I wrote my first pass at my own VDP routines.  I don't like the routines supplied with the console, mostly because they are slow and designed for memory savings over speed.

 

Anyway, clearing the screen, IMO, should be done directly (with the few lines of custom code that is takes), or with a VSMW-like routine.  The routine is good for initializing any chuck of VRAM to a known value.  I also don't like or use BLWP, I prefer to use BL.  Again, these are just my personal preferences, you are welcome to use (or not) any, none, or all of this as you like.

 

WORKSPACE       EQU >8300               // Workspace, always in 16-bit scratch pad RAM

* R0LB must be defined in the main assembly program to the address of
* the CPU R0 LSB address.  For example:
R0LB            EQU WORKSPACE+1         // R0 low byte, required by lib_vdp

* VDP memory mapped port addresses
*
VDP_READ        EQU >8800               // VDP read data
VDP_STATUS      EQU >8802               // VDP status
VDP_WRITE       EQU >8C00               // VDP write data
VDP_ADDR        EQU >8C02               // VDP set read/write address

 

*********************************************************************
*
* VDP Single Byte Multiple Write
*
* R0   dst: write-to address in VDP RAM
* R1   src: MSB of R1 will be written to VDP RAM
* R2   len: number of times to write the MSB byte of R1 to VDP RAM
*
* R0 is modified, but can be restored with: ANDI R0,>3FFF
* R2 is changed to 0
*
VSMW
    MOVB    @R0LB,@VDP_ADDR             // Send low byte of VDP RAM write address
    ANDI    R0,>3FFF                    // Clear the two MSbits to 00
    ORI     R0,>4000                    // Set read/write bits 14 and 15 to write (01)
    MOVB    R0,@VDP_ADDR                // Send high byte of VDP RAM write address
VSMWLP
    MOVB    R1,@VDP_WRITE               // Write byte to VDP RAM
    DEC     R2                          // Byte counter
    JNE     VSMWLP                      // Check if done
    B       *R11
*// VSMW

 

  • Like 3
Link to comment
Share on other sites

1 hour ago, GDMike said:

Oh btw, I can't use any bl code, I'm already bl to this routine. How to work around that?

 

Easy: 

  • You save R11 to an unused register or memory location before you execute the second BL. You restore R11 when your BLed function returns—or
  • If you need more than 2 levels of branching, you can set up a stack that pushes/pops your return (R11) addresses.

...lee

  • Thanks 1
Link to comment
Share on other sites

3 hours ago, GDMike said:

Oh btw, I can't use any bl code, I'm already bl to this routine. How to work around that?

 

2 hours ago, Lee Stewart said:

If you need more than 2 levels of branching, you can set up a stack that pushes/pops your return (R11) addresses.

 

As Lee pointed out, make a pseudo stack.  There was a lot of discussion about subroutine calling in the Assembly thread around here:

 

https://atariage.com/forums/topic/162941-assembly-on-the-994a/?do=findComment&comment=2094076

 

Here is a skeleton I use for a pseudo stack from that thread:

 

**
* Scratch pad RAM use - Variables
*
*           >8300             * Workspace
*           >831F             * Bottom of workspace
STACK  EQU  >8320             * Subroutine stack, grows down (8 bytes)
*           >8322             * The stack is maintained in R10 and
*           >8324             * supports up to 4 BL calls
*           >8326

. . .

*      Initialize the call stack and Finite State Machine (FSM)
      LI   R10,STACK         * Set up the stack pointer

. . .

*********************************************************************
*
* <subroutine skeleton>
*
SKEL
      MOV  R11,*R10+         * Push return address onto the stack

*      Subroutine code here ...

      DECT R10               * Pop return address off the stack
      MOV  *R10,R11
      B    *R11
*// SKEL

 

  • Thanks 1
Link to comment
Share on other sites

I prefer to make stacks that grow towards lower addresses, not higher. The reason for this is simply that the TMS 9900 only has post-autoincrement. I want the stack pointer to point to top of stack all the time. When the stack grows towards higher addresses, like in the example above, the stack pointer points at the next available address in the stack, not the top of stack, after a push.

 

So this is how I implement a stack

EQU		SP		10
STACK	BES		32

LI		SP		STACK
PUSH	DECT	SP
		MOV		R11,*SP

POP		MOV		*SP+,R11

* Read the current value from the stack
		MOV		*SP,R0

Having immediate access to a return link via the stack pointer isn't too important, but if you also use the stack for other purposes, it's handy. Then a MOV *SP saves space and time, compared to MOV @-2(SP).

  • Like 3
Link to comment
Share on other sites

9 hours ago, GDMike said:

This will take me awhile to figure out. Then port it over. But it's an idea. 

I was getting close with mine but I don't know what I'm doing wrong, so since yours works, ill try to understand it.

You're like the shell answer guy, and quick too!

Ok, it's almost bed time and I still gotta clean up, chars all over the place. Ty. I'll start on this code tommorow and toss mine out.

Oh btw, I can't use any bl code, I'm already bl to this routine. How to work around that?

Thank you. So happy to see working code.

Several people have answered.

 

I do something like this:

 

R1R11       DATA 0

RINE1       MOV  R11,@R1R11

                BL     @RINE2

                MOV  @R1R11,R11

                B       *R11

R2R11       DATA 0

RINE2      MOV    R11,@R2R11

               (code)

               MOV    @R2R11,R11

               B        *R11

 

This can be repeated to as many levels as you need.  Basically, store R11 for your return as you go into each routine, then restore it to back up one level when you want to exit to the previous routine.

 

Beery

 

 

  • Thanks 1
Link to comment
Share on other sites

9 hours ago, GDMike said:

Oh btw, I can't use any bl code,

 

Although I assumed/advised to BL into my routine...

 

In the test example, I began execution at ENTRY, by using E7D12, then let the PC increment on it's own!

 

The RT at the end of my routine ...RETURNED execution to EASYBUG, which had handed-off execution to my routine using a BL.

 

So, should work either way.:ponder: Just remove/replace RT.

:)

  • Like 1
Link to comment
Share on other sites

EQU		SP		10
STACK	BES		32

LI		SP		STACK

* make these macros
PUSH	DECT	SP
		MOV		<REG>,*SP

POP		MOV		*SP+,<REG>

* "normal" sub-routines where you know it will not be nested
LEAF1   * CODE...		* A leaf is a fast in and out
		* CODE ...
        RT

* manual coding
SUB1    DECT 	SP
		MOV		R11,*SP
        * CODE...
        * CODE...
        MOV		*SP,R11
        RT 
        
* PUSH/POP MACROS        
SUB2    PUSH R11
		* CODE ...
        * CODE ...
        POP R11
        RT 
        
* FANCIER MACROS
ENTER 	PUSH R11

RETURN  POP R11
		RT

SUB3   	ENTER
		* CODE ..
        * CODE ..
        RETURN
        

I don't use the conventional tools but I make use of Assembler macros to help my brain.  I  have written  up the ideas in "psuedo" Assembler code.

(Do people use a macro-assembler for the TI-99?)

 

The concept is that a "leaf" is the native 9900 sub-routine and you keep them for those little fast things and you know you will never nest them.

For bigger sub-routines I like the stack that Apersson created but I automate it with macros.  With a good macro assembler you can get the code to a pretty hi level.

(Some Assembler coders might call that heresy) :) 

 

 

 

  • Like 1
Link to comment
Share on other sites

I'm using the macro assembler from Mr. Green.

But I don't know anything about using or setting up the"macro" part. I always thought it was geared towards GPL and it's function. 

I use it because it's graphical and it shows my actual line number for errors within a module.

But, I'm still studying automations code. I really like to "Know" it before I use it. So I'll be digging into it today. I can't wait.

I'm still pissed that my code didn't work, I mean it kinda did, incrementing through the "page" and finding the first 2 characters, but that was where I stopped.

I'll switch gears and start looking at how his code works, but still amazing how fast he came up with it. Like..hmm like the incredibles, 

  • Thanks 1
Link to comment
Share on other sites

*DELETE
TEST   TEXT 'COMEFINDMEBRO' *DEL
       AORG >7D00           *DEL
QUERI  TEXT 'COMEFINDMEBRO' *SET

What do you mean "DEL"

Btw, I padded my search code like you said, with zeros, I had used >20 spaces, but changed. And presented the following code below. I'm just trying to follow this.

I've changed the test for true with a simple "*" if false and an "A" for true.

So I'm not using my old test code. So can I ignore the test code altogether that you included?

 I'm jumping out at  outno and outyes for test.

I'm not grasping what's going on, if jumping out at outyes,outno - is this a first occurrence or a overall search completion?

I'm assuming it's a first occurrence??

Because, when I ran the code it took, hmm about 15 secs for a result of yes, then I stopped and reran and got a result of yes within 2 secs with a yes result on a non findable word search.

S32P1 is set as this:

S32P1 EQU >23A4

And libad7 contains "searchword" with 2 zeros following

IMG_20200701_112154785_HDR.jpg

IMG_20200701_112205219.jpg

Edited by GDMike
  • Like 1
Link to comment
Share on other sites

3 hours ago, TheBF said:

For bigger sub-routines I like the stack that Apersson created but I automate it with macros.

I used to do the same. The first macro-capable assembler I had for the 99/4A was the assembler in the p-system. I made exactly the same kind of macros as you do, just with different mnemonics.

 

Another advantage with implementing the stack growing towards lower addresses is that if you also use recursion in your programs, you typically want to push (space for) function results and local variables onto the stack. It feels more intuitive to address local variables with positive offsets from the top of stack.

  • Like 2
Link to comment
Share on other sites

5 hours ago, GDMike said:

What do you mean "DEL"

Btw, I padded my search code like you said, with zeros, I had used >20 spaces, but changed. And presented the following code below. I'm just trying to follow this.

I've changed the test for true with a simple "*" if false and an "A" for true.

So I'm not using my old test code. So can I ignore the test code altogether that you included?

 I'm jumping out at  outno and outyes for test.

I'm not grasping what's going on, if jumping out at outyes,outno - is this a first occurrence or a overall search completion?

I'm assuming it's a first occurrence??

Because, when I ran the code it took, hmm about 15 secs for a result of yes, then I stopped and reran and got a result of yes within 2 secs with a yes result on a non findable word search.

S32P1 is set as this:

S32P1 EQU >23A4

And libad7 contains "searchword" with 2 zeros following

The "DATA 0" in-between SEARCH and VALID, can be (DEL)ETED. Those were the zeros following the search term's buffer. I hope you have those following your buffer at LIBAD7(not shown)!

 

The VALID and CMPRE lines should be transposed, as the data statement shouldn't be executed, although that doesn't look like an issue either. Hmm.

 

You say... R4=23A4, R6=0000. That will search from >2384(in LOWMEM) to >FFFF(end of HIGHMEM). I searched from >A000 thru >FFFF. A "nothing found"(result false) search only took a couple seconds. A successful search(result yes) went faster. So 15 seconds sounds wrong.

 

Where/how are you placing the string to be found? Search is case-sensitive!

 

The other changes you've made, do look reasonable. But than again, I haven't eaten yet!:D

 

Perhaps I'll re-run the test, and try to capture a couple debug screens, watching the registers from there, is how I viewed the results, since EASYBUG trashes R4, after the return.

 

Oh, first occurrence? Yes.

 

I'd try to comment the code for you, but I'm a bit overloaded today, and behind on chores from the last few days too!:ponder:

 

EDIT: Above I stated; The VALID and CMPRE lines should be transposed. I meant to say; The SEARCH and VALID lines, should be transposed.:ponder:

Edited by HOME AUTOMATION
,
  • Thanks 1
Link to comment
Share on other sites

Thx. I think I can make it from here, I'll let ya know. Yes, I've added the zeros at the end of my input word as you recommended I don't need to search from A000 - FFFF just a page because of different things going on. It's going to be pages I have setup, and their not all consecutive on 860 byte boundary.

  • Like 1
Link to comment
Share on other sites

4 hours ago, HOME AUTOMATION said:

The "DATA 0" in-between SEARCH and VALID, can be (DEL)ETED. Those were the zeros following the search term's buffer. I hope you have those following your buffer at LIBAD7(not shown)!

 

The VALID and CMPRE lines should be transposed, as the data statement shouldn't be executed, although that doesn't look like an issue either. Hmm.

 

You say... R4=23A4, R6=0000. That will search from >2384(in LOWMEM) to >FFFF(end of HIGHMEM). I searched from >A000 thru >FFFF. A "nothing found"(result false) search only took a couple seconds. A successful search(result yes) went faster. So 15 seconds sounds wrong.

 

Where/how are you placing the string to be found? Search is case-sensitive!

 

The other changes you've made, do look reasonable. But than again, I haven't eaten yet!:D

 

Perhaps I'll re-run the test, and try to capture a couple debug screens, watching the registers from there, is how I viewed the results, since EASYBUG trashes R4, after the return.

 

Oh, first occurrence? Yes.

 

I'd try to comment the code for you, but I'm a bit overloaded today, and behind on chores from the last few days too!:ponder:

 

EDIT: Above I stated; The VALID and CMPRE lines should be transposed. I meant to say; The SEARCH and VALID lines, should be transposed.:ponder:

I see a couple things going on, I have to limit the search, or put a counter in between at every 840 characters passed to mark a page, because I've got to know where the word is located if found by page number.

Easy nuff, and I'm not looking to find another instance of the search word, just a one shot.

Edited by GDMike
Link to comment
Share on other sites

Ok, I'm sorry to say, I've thrown out that example code from automation...man, I couldn't grasp it, but I continued with what I supplied and made a couple changes on my compare and I believe it's working.  I'll provide the code I came up with and the video. 

Link to comment
Share on other sites

No, I did more testing and I changed my code and got it so screwed up that I am dissatisfied with the approach. Then I fell down, slipped on wet algae latened concrete in front of my garage back door when I was trying to water the plants this morning early and I was even in tennis shoes, I still fell, or say more like my feet went way up. So double pissed, I met the air conditioner guys a couple hours later and they can't fix my ac until next week. So I'll punt.

Edited by GDMike
Link to comment
Share on other sites

I'm feeling your pain all the way up here. 

There must a refreshing beverage of some kind in your state to ease your pain for the 4th of July.

Hope you can stay cool enough. It's hotter than hades up here the last few days. (88+ F)  

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...