Shop OBEX P1 Docs P2 Docs Learn Events
LMM P2 Debugger - Uses Serial Tx & Rx for debugging single cog programs — Parallax Forums

LMM P2 Debugger - Uses Serial Tx & Rx for debugging single cog programs

Cluso99Cluso99 Posts: 18,069
edited 2013-10-03 23:04 in Propeller 2
Update 28 May 2013:
The latest code v0.86 is posted on threads #275 & #276
http://forums.parallax.com/showthread.php/146688-LMM_SerialDebug-Simple-Serial-Tx-amp-Rx-for-debugging-single-cog-programs?p=1184834&viewfull=1#post1184834
The latest writeups are on posts just prior to these.
The debugger now includes simple instruction decoding as well as display/modify of both hub and cog ram.
I have also changed the thread title to better reflect the debugger.

Update 18 APr 2013:
Works on both DE0 & DE2 emulators.
Additional features and versions on page 3.

Update 24 Mar 2013:
Latest code adds RX mode to receive a serial character. This now permits the users cog code to interact simply with the serial terminal.
(see v0.22 below, and in post #33)

Update 20 Mar 2013:

Here is working v0.10 code for debugging single cog pasm.

Requirements: 11 longs of user cog space, plus 2 longs per call.
The cog space is an LMM execution unit and the hub space (currently set to $1000, but can be changed in the CON section) stores the LMM debug code.

The current code permits the user to call the routine to send an 8bit character to the serial port (transmit only for now, at least).
Baud can be set in the CON section.
Z & C flags are saved and restored.

LMM_SerialDebugger.spin

Previous posting...

Here is a simple Tx program using P90 and USB & PST on the DE0.
It uses 12/13 longs of COG ram plus 2 longs for each tx/debug call.

It is based on Chips ROM Monitor.

I used the constant #511 which gives ~2% higher baud rate than 115,200. It seems to work fine, and saves a long.

Serial_test.spin
«13456710

Comments

  • TubularTubular Posts: 4,702
    edited 2013-03-17 17:35
    Hi Cluso,

    I look forward to trying this out.

    Another way for doing serial might be the SNDSER/ RCVSER commands designed for inter-prop communication. Its not clear whether they can be adapted for start & stop bits, etc. We will need to wait for the detail on these
  • RaymanRayman Posts: 14,633
    edited 2013-03-17 18:06
    This looks great. I think I can use this to figure out some things going on in my DE0.
    If I had a DE2, I suppose I could just run monitor in the other cog, but this may be even easier.

    One question, would it be easy to make it 9600 baud?
    Or, do you want to do it fast so as not to mess up video drivers?
    Or, are you trying to save a long that would be needed for the period?

    I think I see you did it to save a long, now that I look closer...
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-17 18:13
    Cluso99 wrote: »
    Here is a simple Tx program using P90 and USB & PST on the DE0.
    It uses 12/13 longs of COG ram plus 2 longs for each tx/debug call.

    It is based on Chips ROM Monitor.
    ' Transmit chr (x)                              ' 12/13 longs
    '
    tx              setp    #_txpin                 ' do it each time to simplify
                    shl     x,#1                    ' insert start bit
                    setb    x,#9                    ' set stop bit
                    getcnt  w                       ' get initial time
    :loop           add     w,#511     'period      ' add bit period to time
                    passcnt w                       ' loop until bit period elapsed
                    shr     x,#1            wc      ' get next bit into c
                    setpc   #_txpin                 ' write c to tx pin
                    tjnz    x,#:loop                ' loop until 10 bits done
    tx_ret          ret
    '
    'period          long    _bitrate               ' 1 bit time (to save long, use #511 which is close enough)
    x               long    0
    w               long    0
    
    I used the constant #511 which gives ~2% higher baud rate than 115,200. It seems to work fine, and saves a long.

    Serial_test.spin
    I just used this to debug my flash loader and it worked great! Thanks!!
  • AribaAriba Posts: 2,690
    edited 2013-03-17 20:03
    Rayman wrote: »
    This looks great. I think I can use this to figure out some things going on in my DE0.
    If I had a DE2, I suppose I could just run monitor in the other cog, but this may be even easier.

    One question, would it be easy to make it 9600 baud?
    Or, do you want to do it fast so as not to mess up video drivers?
    Or, are you trying to save a long that would be needed for the period?

    I think I see you did it to save a long, now that I look closer...

    If you not want save a long you can make any baudrate:
    ' Transmit chr (x)                              ' 12/13 longs
    '
    tx              setp    #_txpin                 ' do it each time to simplify
                    shl     x,#1                    ' insert start bit
                    setb    x,#9                    ' set stop bit
                    getcnt  w                       ' get initial time
    :loop           add     w,period                ' add bit period to time
                    passcnt w                       ' loop until bit period elapsed
                    shr     x,#1            wc      ' get next bit into c
                    setpc   #_txpin                 ' write c to tx pin
                    tjnz    x,#:loop                ' loop until 10 bits done
    tx_ret          ret
    '
    period          long    60_000_000 / 9600       ' 1 bit time for Baudrate
    x               long    0
    w               long
    '
    
    Andy
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-17 22:34
    Thanks guys.
    As you have seen, yes you can make it any baud you like. I only posted a snippet from the file, so the baud and clock are defined as constants at the top.

    Here is the next bit of code I used to locate my file in hub (just wanted to check it was there, hey hey) at $E80...
    DAT
                    org     0
                    jmp     #start
                    long    0[3]
                    long    $67452301               ' HUB $E90... s/be: 0123456789ABCDEF 0123456789ABCDEF
                    long    $EFCDAB89               ' monitor E80.EFF will display hub
                    long    $67452301
                    long    $EFCDAB89
     
    start        
    '               setcog  #0        
                    coginit monitor_pgm,monitor_ptr ' init cog0 with monitor
    monitor_pgm     long    $70C                    ' monitor program address
    monitor_ptr     long    90<<9 + 91              ' monitor parameters
     
    'This is how you send a char to the serial port (USB & PST)
    :again          mov     x,#"B"                  ' \ send "B"
                    call    #tx                     ' /
                    mov     x,#"c"
                    call    #tx
                    jmp     #:again
    
    
    

    I am on to getting it working using LMM. So far, seems like I will need 8 longs to do it.

    David: How are you compiling LMM with GCC ?
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-18 04:11
    Cluso99 wrote: »
    David: How are you compiling LMM with GCC ?
    I'm not sure I understand your question. We've had P2 COG and LMM modes working in PropGCC since December and CMM since January if I remember correctly. We don't currently have XMM working although now that I have the flash loader working that's the next thing on my list.

    If you want to build LMM C code for P2 just use the version of PropGCC that I posted a month ago with the -mp2 command line option. Now that I have flash loading working I guess I should make another release.

    What I've been working on recently is getting C programs to boot from flash at reset. As you know, the ROM loader only loads a single COG image. That has to contain a second-stage loader that pulls in the rest of the hub image. That is what I got working last night.
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-18 19:12
    David: re LMM

    I need to compile a pasm program for the P2 that has LMM code. What I am trying to do is put the tx routine (above in the serial program) as an LMM routine resident in hub, and then have a simple routine in COG that will run an LMM routine when required. This is for using the serial output to debug COG code with a minimal footprint in the cog. This way, the hub LMM routines could be expanded with calls frm the cog LMM routine.

    So I am looking for a simpler way to compile LMM. Currently I have to specifically code LMM instructions and use P2 pnut.exe. Is there a better way by using GAS in PropGCC without learning GCC?

    Here is an example of where I am currently up to (there is a bug - no output)
    CON
      _clkmode = xinput
      _xinfreq = 60_000_000
      _baud    = 115_200
      _bitrate = _xinfreq / _baud
      _period  = 511                                ' approx bitrate for 60MHz & 115,200 baud (permits immediate operand)
      _txpin   = 90                                 ' P90=SI
      _rxpin   = 91                                 ' P91=SO
      _LMM_org = $1000                              ' hub addr of the LMM routines
      
    DAT
                    org     0
    '$E80
    start        
    'This is how you send a char to the serial port (USB & PST)
    :again          mov     LMM_x,#"L"              ' \ send "L"
                    call    #LMM_call               ' /
                    mov     LMM_x,#"c"
                    call    #LMM_call
                    jmp     #:again
    '------------------------------------------------------------------------------------------------
    ' LMM execution call loop
    '------------------------------------------------------------------------------------------------
    LMM_call        rdlong  LMM_opcode,LMM_PC       ' read next hub instruction to be executed (pointed to by LMM_PC)
                    add     LMM_PC,#4               ' inc LMM_PC (to next hub instr)
    LMM_opcode      nop                             ' execute the hub instr
                    jmp     #LMM_call               ' loop again
    LMM_call_ret    ret                             ' calling return address
    LMM_PC          long    _LMM_org                ' the LMM PC (program counter) hub address (initial =$1000)
    LMM_x           long    0                       ' parameter passed to/from LMM routine
    LMM_w           long    0                       ' workarea for LMM_call
    '------------------------------------------------------------------------------------------------
    ' This will be the LMM program that will reside at hub $1000...
    '------------------------------------------------------------------------------------------------
                    long    0[((_LMM_org-$E80)/4) - $] ' fill to _LMM_org
                    org     0
    '
    ' Transmit chr (x)                              ' 12/13 longs
    '
    HUB_tx          setp    #_txpin                 ' do it each time to simplify
                    shl     LMM_x,#1                ' insert start bit
                    setb    LMM_x,#9                ' set stop bit
                    getcnt  LMM_w                   ' get initial time              \ start bit
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b0
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b1
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b2
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b3
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b4
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b5
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b6
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ b7
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    getcnt  LMM_w                   ' get initial time              \ stop bit
                    add     LMM_w,#_period          ' add bit period to time        |
                    passcnt LMM_w                   ' loop until bit period elapsed |
                    shr     LMM_x,#1            wc  ' get next bit into c           |
                    setpc   #_txpin                 ' write c to tx pin             /
                    jmp     #LMM_call_ret           ' return to COG User code after 10 bits done (can be indirect to speedup)
                    rdlong  LMM_PC,_hub_tx          ' modifies the LMM PC (causes jmp #tx next time called)
    _hub_tx         long    _LMM_org                ' points to hub addr of HUB_tx routine
    
  • TubularTubular Posts: 4,702
    edited 2013-03-18 19:40
    Cluso you might be having the same issue I am seeing with setpc and friends... it doesn't seem to work quite as you'd expect and I'm trying to understand why never mind, my own stupid fault...
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-18 20:07
    Cluso99 wrote: »
    David: re LMM

    I need to compile a pasm program for the P2 that has LMM code. What I am trying to do is put the tx routine (above in the serial program) as an LMM routine resident in hub, and then have a simple routine in COG that will run an LMM routine when required. This is for using the serial output to debug COG code with a minimal footprint in the cog. This way, the hub LMM routines could be expanded with calls frm the cog LMM routine.

    So I am looking for a simpler way to compile LMM. Currently I have to specifically code LMM instructions and use P2 pnut.exe. Is there a better way by using GAS in PropGCC without learning GCC?
    Well, GAS can certainly assemble LMM code but I don't know if it will be any better than PNut. I've only really used it to assemble the output of the C compiler and to compile COG programs not LMM programs. What features are you looking for that would make the code easier to write? Are you looking for pseudo-ops for the LMM macro instructions like your LMM_call_ret? I think GAS has some of those but they are geared to the GCC LMM kernel which is probably to heavy for what you're trying to do.
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-18 21:46
    David Betz wrote: »
    Well, GAS can certainly assemble LMM code but I don't know if it will be any better than PNut. I've only really used it to assemble the output of the C compiler and to compile COG programs not LMM programs. What features are you looking for that would make the code easier to write? Are you looking for pseudo-ops for the LMM macro instructions like your LMM_call_ret? I think GAS has some of those but they are geared to the GCC LMM kernel which is probably to heavy for what you're trying to do.
    I was looking for macro calls for jumps in particular, where the value has to be held in the following long (or some other method).
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-19 05:32
    Cluso99 wrote: »
    I was looking for macro calls for jumps in particular, where the value has to be held in the following long (or some other method).
    I don't believe that GAS supports user-defined macros other than what you can get from the C preprocessor. If you name your file with a capital "S" extension (something like "foo.S") rather than a lowercase "s" the C preprocessor will be run on it before it is assembled.
  • jazzedjazzed Posts: 11,803
    edited 2013-03-19 07:38
    Cluso99 wrote: »
    I was looking for macro calls for jumps in particular, where the value has to be held in the following long (or some other method).

    The macros are described in the propeller section of the assembler manual: as.pdf.

    https://code.google.com/p/propgcc/downloads/detail?name=as.pdf
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-19 07:42
    jazzed wrote: »
    The macros are described in the propeller section of the assembler manual: as.pdf.

    https://code.google.com/p/propgcc/downloads/detail?name=as.pdf
    Thanks but I was under the impression that Cluso wanted to write his own macros. He's not using our LMM kernel, he's using a very simple one he designed for a minimal footprint debug stub. I may be wrong though.
  • jazzedjazzed Posts: 11,803
    edited 2013-03-19 08:51
    David Betz wrote: »
    Thanks but I was under the impression that Cluso wanted to write his own macros. He's not using our LMM kernel, he's using a very simple one he designed for a minimal footprint debug stub. I may be wrong though.

    Well I guess I misunderstood "looking for macros" then. Whatever.
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-19 17:28
    jazzed & David:
    I just looked at the GAS document (thanks Steve) and it would seem to do what I require. The BRS & BRW are the macros (or extended instructions if you like) I would need.
    What do they expand to?
    Presuming they perform an add/subtract or move to the PC, can the location of the PC in cog be simply relocated???
    Is there a description of the LMM execution instructions you are using? I see you predefine about 18 regsters beginning at Cog $0.

    I am using a very simple and compact LMM execution unit. The hardest part is compiling the LMM debug code that will reside in hub and be executed by this LMM execution unit - currently this has to be hand crafted.

    This is what I am trying to do...
    1. Have a minimal LMM execution unit that can be added to anyones cog pasm code (minimum footprint)
    2. When they execute a CALL to my LMM execution unit, it performs some specific debugging (currently just transmits a character passed to it, to the serial port, and returns to the users code).
    3. When this works, I may add some extra features, such as calling parameters in addition to the simple char to tx.
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-19 17:46
    Cluso99 wrote: »
    jazzed & David:
    I just looked at the GAS document (thanks Steve) and it would seem to do what I require. The BRS & BRW are the macros (or extended instructions if you like) I would need.
    What do they expand to?
    Presuming they perform an add/subtract or move to the PC, can the location of the PC in cog be simply relocated???
    Is there a description of the LMM execution instructions you are using? I see you predefine about 18 regsters beginning at Cog $0.

    I am using a very simple and compact LMM execution unit. The hardest part is compiling the LMM debug code that will reside in hub and be executed by this LMM execution unit - currently this has to be hand crafted.

    This is what I am trying to do...
    1. Have a minimal LMM execution unit that can be added to anyones cog pasm code (minimum footprint)
    2. When they execute a CALL to my LMM execution unit, it performs some specific debugging (currently just transmits a character passed to it, to the serial port, and returns to the users code).
    3. When this works, I may add some extra features, such as calling parameters in addition to the simple char to tx.
    Looks like BRS expands to an ADD or SUB of a constant and BRW expands to a "jmp #__LMM_JMP" followed by a constant.

    I don't think it is possible to move the registers to a different location. The LMM kernel depends on R0 being at COG location 0.
  • jazzedjazzed Posts: 11,803
    edited 2013-03-19 17:52
    Cluso99 wrote: »
    ...This is what I am trying to do...
    1. Have a minimal LMM execution unit that can be added to anyones cog pasm code (minimum footprint)
    2. When they execute a CALL to my LMM execution unit, it performs some specific debugging (currently just transmits a character passed to it, to the serial port, and returns to the users code).
    3. When this works, I may add some extra features, such as calling parameters in addition to the simple char to tx.

    That's a pretty neat idea. Sort of the reverse of fcache.

    Eric may be able point to more info on the LMM engine.
    The only doc I know of is in the repository here: http://propgcc.googlecode.com/hg/doc/Memory.html
  • AribaAriba Posts: 2,690
    edited 2013-03-19 22:03
    Hi Cluso

    Here is a working version of your LMM trial code.
    You need 3 delay slots between modifying a cog register and executing it. You should perhaps read the long LMM-P2 thread from Bill Henning, where we have discovered the LMM possibilities of P2.

    Doing the LMM code in PNUT is not that hard, if you use realtive jumps.
    CON
      _clkmode = xinput
      _xinfreq = 60_000_000
      _baud    = 115_200
      _bitrate = _xinfreq / _baud
      _period  = 511                                ' approx bitrate for 60MHz & 115,200 baud (permits immediate operand)
      _txpin   = 90                                 ' P90=SI
      _rxpin   = 91                                 ' P91=SO
      _LMM_org = $1000                              ' hub addr of the LMM routines
      
    DAT
                    org     0
    '$E80
    start        
    'This is how you send a char to the serial port (USB & PST)
    :again          mov     LMM_x,#"L"              ' \ send "L"
                    call    #LMM_call               ' /
                    mov     LMM_x,#"c"
                    call    #LMM_call
                    jmp     #:again
    '------------------------------------------------------------------------------------------------
    ' LMM execution call loop
    '------------------------------------------------------------------------------------------------
    LMM_call        mov     LMM_PC,LMM_begin        ' set PC to begin of code
    LMM_loop        rdlong  LMM_opcode,LMM_PC       ' read next hub instruction to be executed (pointed to by LMM_PC)
                    add     LMM_PC,#4               ' inc LMM_PC (to next hub instr)
                    nop
                    nop
    LMM_opcode      nop                             ' execute the hub instr
                    jmp     #LMM_loop               ' loop again
    LMM_call_ret    ret                             ' calling return address
    LMM_PC          long    0                       ' the LMM PC (program counter) hub address (initial =$1000)
    LMM_x           long    0                       ' parameter passed to/from LMM routine
    LMM_w           long    0                       ' workarea for LMM_call
    LMM_begin       long    @HUB_tx+$0E80           ' the LMM code address
    LMM_dly         long    15_000_000              ' 1/4 second
    '------------------------------------------------------------------------------------------------
    ' This will be the LMM program that will reside at hub $1000...
    '------------------------------------------------------------------------------------------------
                    long    0[((_LMM_org-$E80)/4) - $] ' fill to _LMM_org
    '
    ' Transmit chr (x)
    '
    HUB_tx          setp    #_txpin                 ' do it each time to simplify
                    shl     LMM_x,#1                ' insert start bit
                    setb    LMM_x,#9                ' set stop bit
                    getcnt  LMM_w                   ' get initial time
    {loop}          add     LMM_w,#_period          ' add bit period to time
                    passcnt LMM_w                   ' wait bit period
                    shr     LMM_x,#1   wc,wz        ' get next bit into c 
                    setpc   #_txpin                 ' write c to tx pin
            if_nz   sub     LMM_PC,#5*4             ' loop until stop bit sent
                    add     LMM_w,LMM_dly           ' wait 1/4 second
                    passcnt LMM_w
                    jmp     #LMM_call_ret           ' return to PASM code
    

    Andy
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-19 22:05
    Here is working v0.10 code for DE0 :)

    Currently uses 11 longs in the users cog, plus 2 longs per call. The debug tx routine currently lives in hub at $1000 but his can be changed.

    Z & C flags are saved and restored. BTW this is so simple to do - thanks Chip!

    LMM_SerialDebugger.spin
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-19 22:23
    Thanks Andy, but seems we crossed posted :)

    The passcnt instruction should follow the setpc instruction and then the 1/4s delay at teh end is not required.
    I like your sub LMM_PC,#5*4 instruction.
    There are a few thing that I can do to shorten the cog code, but for now it is fine.

    All:

    I am thinking about a calling method where I can call a number of different LMM routines. Perhaps I can use the LMM_x register to use the upper bits for a calling method number.

    Just in case you didn't realise, the method to save and restore the Z & C flags is so simple!!! Thanks Chip :)
                  call  #routine    wz,wc           ' call a subroutine and save the Z & C flags
                  ...
    
    routine       ...
                  ...
    routine_ret   ret               wz,wc           ' return and restore the Z & C flags
    
    
    
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-20 02:05
    Quick question...

    I am trying to add some function calls to my serial debugger, such as outputting hex, etc.

    How do I do a call in LMM? I need to save the return address but how???
  • AribaAriba Posts: 2,690
    edited 2013-03-20 03:05
    If you can live with only one Subroutine level then this may work:
    ...
            mov     retaddr, LMM_PC    'call
            rdlong  LMM_PC, LMM_PC
            long    <Subr_addr>
            ...
    
    
    Subroutine
            ...
            add	retaddr,#8        'return
    	mov	LMM_PC, retaddr
    
    This only needs one reserved register called: retaddr
    otherwise you will need PASM helper routines for call and return, which handels a stack (easy on P2 if you can use the stack ram).

    Andy
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-20 05:04
    Thanks Andy. I just couldn't get my head around the rdlong lmm_pc,lmm_pc instruction.

    I don't want to use cog resources such as the stack, etc, so that there are minimal restrictions on the users program.
  • AribaAriba Posts: 2,690
    edited 2013-03-20 05:45
    The RDLONG LMM_PC,LMM_PC is an absolute jump. When this instruction is executed the PC points already to the next instruction, which is the long with the subroutine address. So this address gets loaded into the PC. The next LMM loop executes then the first instr. of the subroutine, the long <subr_address> is never executed. The return from the subroutine jumps then right after this long back to the main code.

    If you try it dont forget to add $E80 to every subroutine address:

    rdlong LMM_PC,LMM_PC
    long @subr1+$E80

    Andy
  • David BetzDavid Betz Posts: 14,516
    edited 2013-03-20 06:56
    Cluso99 wrote: »
    Thanks Andy, but seems we crossed posted :)

    The passcnt instruction should follow the setpc instruction and then the 1/4s delay at teh end is not required.
    I like your sub LMM_PC,#5*4 instruction.
    There are a few thing that I can do to shorten the cog code, but for now it is fine.

    All:

    I am thinking about a calling method where I can call a number of different LMM routines. Perhaps I can use the LMM_x register to use the upper bits for a calling method number.

    Just in case you didn't realise, the method to save and restore the Z & C flags is so simple!!! Thanks Chip :)
                  call  #routine    wz,wc           ' call a subroutine and save the Z & C flags
                  ...
    
    routine       ...
                  ...
    routine_ret   ret               wz,wc           ' return and restore the Z & C flags
    
    
    

    I hadn't noticed that feature. Thanks for pointing it out!
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-20 17:42
    Thanks for the info Andy.

    My thoughts are to build a second version with the ability to contain additional LMM code to output...
    1. Tx an 8 bit character in X (where X is LMM_X) - same as done
      • MOV LMM_X,#"7"
      • CALL LMM_TX WZ,WC
    2. Preset a mode (LMM_Y) with a value for a Function to be performed
    [LIST=|INDENT=1]
    [*]MOV LMM_Y,#mode+n 'n is a nibble parameter as required by some modes (only needs to be preset once)
    [*]MOV LMM_X,[#]value
    [*]CALL LMM_FN WZ,WC
    [*]This mode is static (only needs to be set once) using "MOV LMM_Y,#mode+n"
    • mode is in bits 8..4; n is a nibble parameter 0-15 in bits 3..0
    [*]modes are...
    • ASCII
      • X is lower 8 bits
      • X=$00..$1F displays "<xx>" where xx is the hex value of X
      • X=$20..$7E displays the ascii character
      • X=$7F..$FF displays "<xx>" where xx is the hex value of X
    • HEX
      • X is 32 bits
      • n sets the number of hex digits displayed
    • DECIMAL
      • X is 32 bits
      • n sets the number of decimal digits displayed
    • BINARY
      • X is 32 bits, lowest bits used
      • n sets the number of nibbles (quad bits) displayed
    • STRING
      • X is the address of the string, $0 terminated
        • if X<511 then the address is in COG RAM
        • if X>511 then the address is in HUB RAM
    [/LIST]
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-21 06:41
    Here is the latest installment. Some code tidyup and code optimising is still to be done.

    This does...
    1. sends characters to the serial port
    2. options for displaying in hex and hex reversed (ie. swaps endian), and option for no. of digits(nibbles) 1-8
    3. options for displaying strings, nul terminated, residing in hub
    4. apart from ~15 longs, all code resides in hub and runs in LMM mode
    5. mode only needs to be set each time the mode is required to change

    LMM_SerialDebugger_010c.spin
  • Bill HenningBill Henning Posts: 6,445
    edited 2013-03-21 06:51
    Thanks, nice work!

    This will be very useful for everyone debugging cog code.
    Cluso99 wrote: »
    Here is the latest installment. Some code tidyup and code optimising is still to be done.

    This does...
    1. sends characters to the serial port
    2. options for displaying in hex and hex reversed (ie. swaps endian), and option for no. of digits(nibbles) 1-8
    3. options for displaying strings, nul terminated, residing in hub
    4. apart from ~15 longs, all code resides in hub and runs in LMM mode
    5. mode only needs to be set each time the mode is required to change

    LMM_SerialDebugger_010c.spin
  • Cluso99Cluso99 Posts: 18,069
    edited 2013-03-22 05:13
    Thanks Bill.

    Here is the latest v0.20 update...
    1. Two basic call types are provided
      • LmmTx
        • Displays the char copied to lmm_x
        • Does not have a mode setting
        • This call does not interfere with LmmCalls and their modes
      • LmmCall
        • Displays chars according to the mode setting
        • Modes are:
          • ASCII
            • Displays visible chars $20..$7E (" ".."~")
            • All other chars are displayed in hex n the form "<xx>"
          • STRING
            • Displays a "nul" terminated string (currently only from hub)
          • HEX
            • Displays hex digits of a value supplied in a long.
            • Digits selectable from 1..8 in mode call.
          • HEXREV
            • Same as HEX, but reverses the byte order first.
          • DUMP
            • Displays 4 longs (16 bytes) in HEX and ASCII plus address (currently only from hub)
            • Similar line display to the P2 Rom Monitor
            • Once mode and address are set, subsequent single instruction calls display the next line address.
          • Mode
            • Once set, the mode remains set until a new mode is required (i.e. "sticky")
            • Mode is set in bits b7..4
            • A mode Modifier is set in bits b3..0 (typically to specify number of digits in Hex mode)
    2. Z & C flags are maintained for the user
    3. Currently uses 16 longs in COG
    4. Serial Debug code resides in hub and is executed as LMM.
    5. Uses minimal Prop Cog resources.
    It is my intention to convert this back to Prop1 code later.

    LMM_SerialDebugger_020.spin

    Postedit:
    Current v0.20 hub usage is <1KB (~$3A0) plus the 16 longs (loaded into user cog) and any user calls.
  • Bill HenningBill Henning Posts: 6,445
    edited 2013-03-22 10:08
    Hmmm.... an LmmRx routine would be very useful :)

    (but I am certain you were already planning to add it)
Sign In or Register to comment.