Shop OBEX P1 Docs P2 Docs Learn Events
Spin2 - Page 8 — Parallax Forums

Spin2

1568101118

Comments

  • Cluso99Cluso99 Posts: 18,066
    edited 2020-01-22 01:08
    Chip,
    What on earth do you do with the tabs? They are a nightmare! Nothing lines up (PropTool) :(
    I recall I had the same problem with your P2 ROM code.

    Anyway, I have the source into a form that I think I can use - see attached :smiley:
  • cgraceycgracey Posts: 14,133
    The 'm' and 'n' are variable-write versions of the a..j sequences. The 'x' characters mean 'use the a..j letter here for the 'm' or 'n' version'. So, the 'm' and 'n' sequences represent full a..j sets of sequences. It was a way to keep the number of listed skip sets to ~1/3, without redundancies. That may not be a concept you want to support.
  • Cluso99Cluso99 Posts: 18,066
    cgracey wrote: »
    The 'm' and 'n' are variable-write versions of the a..j sequences. The 'x' characters mean 'use the a..j letter here for the 'm' or 'n' version'. So, the 'm' and 'n' sequences represent full a..j sets of sequences. It was a way to keep the number of listed skip sets to ~1/3, without redundancies. That may not be a concept you want to support.
    I am not quite following but it doesn't matter at this time. I'll get on with doing the simple parts first ;)
  • cgracey wrote: »
    The 'm' and 'n' are variable-write versions of the a..j sequences. The 'x' characters mean 'use the a..j letter here for the 'm' or 'n' version'. So, the 'm' and 'n' sequences represent full a..j sets of sequences. It was a way to keep the number of listed skip sets to ~1/3, without redundancies. That may not be a concept you want to support.

    So, there are 30 different skip patterns for this block: a..j, ma..mj, and na..nj?
  • Cluso99Cluso99 Posts: 18,066
    edited 2020-01-22 08:25
    Here is a sample piece of skip code

    I am thinking that I need to have a format header so that I know what masks need to be created. So what about a header showing which alpha fields need to be created?

    Below are four possibilities. I only need to know when a group of skip instructions begin as that will serve to determine the end of the group, and how many there are ie alphas as a...m below. I can determine that from the header - ie the length of the header is important!!.

    Note that the headers with the alpha characters can be a little confusing with the list below.
    ' Variable pre/post modifiers
    '
                                              '~ -------------------------
                                              '~~a~b~c~d~e~f~g~h~i~j~k~l~m~
                                              '~-a-b-c-d-e-f-g-h-i-j-k-l-m-
                                              '~-A-B-C-D-E-F-G-H-I-J-K-L-M-
    mod_iso         mov     w,x               '~ a b         g   i   k l      a: ++var, var++ (isolated)                      iso     
    mod_psh         pusha   x                 '~ | | c d e f | h | j | | m    b: --var, var-- (isolated)                      push    
                    alti    rd                '~ a b c d e f g h i j k l m    c: ++var        (push)                          rd  
    rd_field        call    #\rdf             '~ a b c d e f g h i j k l m    d: --var        (push)     (pipeline spacer)    rd      
                    xoro32  x                 '~ | | | | | | | | | | | l m    e: var++        (push)                          ??  
                    mov     v,x               '~ | | | | e f | h | j k l m    f: var--        (push)                          post        
                    add     x,#1              '~ a | c | e | | | | | | | |    g: var!!        (isolated)                      ++  
                    sub     x,#1              '~ | b | d | f | | | | | | |    h: var!!        (push)                          --  
                    zerox   x,sz              '~ | | c d | | | | | | | | |    i: var!         (isolated)                      ptr 
                    muxz    x,_FFFFFFFF       '~ | | | | | | g h | | | | |    j: var!         (push)                          !!
                    not     x                 '~ | | | | | | | | i j | | |    k: var\new      (swap)                          !   
                    mov     x,w               '~ | | | | | | | | | | k | |    l: ??var        (isolated)                      swap        
                    alti    wr                '~ a b c d e f g h i j k l m    m: ??var        (push)                          wr  
    wr_field        call    #\wrf             '~ a b c d e f g h i j k l m                               (pipeline spacer)    wr      
            _ret_   mov     x,w               '~ a b | | | | g | i | | l |                                                    iso 
            _ret_   mov     x,v               '~     | | e f   h   j k   m                                                    iso 
                    ret                       '~     c d                                                                      main
    
    I think I favor the dashed header but what do you think?

    BTW trying to get rid of the tabs so the code lines up :(
  • cgraceycgracey Posts: 14,133
    > @AJL said:
    > cgracey wrote: »
    >
    > The 'm' and 'n' are variable-write versions of the a..j sequences. The 'x' characters mean 'use the a..j letter here for the 'm' or 'n' version'. So, the 'm' and 'n' sequences represent full a..j sets of sequences. It was a way to keep the number of listed skip sets to ~1/3, without redundancies. That may not be a concept you want to support.
    >
    >
    >
    >
    > So, there are 30 different skip patterns for this block: a..j, ma..mj, and na..nj?

    Yes. That's the idea. Only the instructions at the beginning and end differ.
  • cgraceycgracey Posts: 14,133
    > @Cluso99 said:
    > Here is a sample piece of skip code
    >
    > I am thinking that I need to have a format header so that I know what masks need to be created. So what about a header showing which alpha fields need to be created?
    >
    > Below are four possibilities. I only need to know when a group of skip instructions begin as that will serve to determine the end of the group, and how many there are ie alphas as a...m below. I can determine that from the header - ie the length of the header is important!!.
    >
    > Note that the headers with the alpha characters can be a little confusing with the list below. ' Variable pre/post modifiers ' '~ ------------------------- '~~a~b~c~d~e~f~g~h~i~j~k~l~m~ '~-a-b-c-d-e-f-g-h-i-j-k-l-m- '~-A-B-C-D-E-F-G-H-I-J-K-L-M- mod_iso mov w,x '~ a b g i k l a: ++var, var++ (isolated) iso mod_psh pusha x '~ | | c d e f | h | j | | m b: --var, var-- (isolated) push alti rd '~ a b c d e f g h i j k l m c: ++var (push) rd rd_field call #\rdf '~ a b c d e f g h i j k l m d: --var (push) (pipeline spacer) rd xoro32 x '~ | | | | | | | | | | | l m e: var++ (push) ?? mov v,x '~ | | | | e f | h | j k l m f: var-- (push) post add x,#1 '~ a | c | e | | | | | | | | g: var!! (isolated) ++ sub x,#1 '~ | b | d | f | | | | | | | h: var!! (push) -- zerox x,sz '~ | | c d | | | | | | | | | i: var! (isolated) ptr muxz x,_FFFFFFFF '~ | | | | | | g h | | | | | j: var! (push) !! not x '~ | | | | | | | | i j | | | k: var\new (swap) ! mov x,w '~ | | | | | | | | | | k | | l: ??var (isolated) swap alti wr '~ a b c d e f g h i j k l m m: ??var (push) wr wr_field call #\wrf '~ a b c d e f g h i j k l m (pipeline spacer) wr _ret_ mov x,w '~ a b | | | | g | i | | l | iso _ret_ mov x,v '~ | | e f h j k m iso ret '~ c d main
    >
    > I think I favor the dashed header but what do you think?
    >
    > BTW trying to get rid of the tabs so the code lines up :(

    Nice.

    Maybe to start a pattern:
    [code]
    ~ + + + + + + + + + +
    [/code]

    To end a pattern:
    [code]
    ~ - - - - - - - - - -
    [/code]

    That could set up the registration of columns and the number of different skip patterns.

    Then, look for either spaces, pipes, or letters in those columns:

    SPACE = ignore
    letter = '0' bit
    | = '1' bit

    Anything other than a SPACE in an unused column within the skip pattern area could be flagged as an error.

    You could make your app such that you paste a block of code into and it produces lines in a text window:

    a %011010
    b %100011
    c %011101

    ...which can be selectively copied to the clipboard for pasting into your program where needed.

    No need for the '~ - - - - - - - - - -' if you're only pasting a block at a time.
  • Cluso99Cluso99 Posts: 18,066
    Chip,
    Nice idea to paste, calculate, and paste back. I’ll keep that in mind while i get it running.
  • cgraceycgracey Posts: 14,133
    edited 2020-01-22 10:56
    Cluso99 wrote: »
    Chip,
    Nice idea to paste, calculate, and paste back. I’ll keep that in mind while i get it running.

    Yeah, no need to process a whole text file. We just need a little utility we can drop a stretch of code into and let it come up with the patterns. Should be way simpler that way, since there's no overall context dependence. Just a little helper app is needed.

    Just have a little app with two edit windows. One window you paste code into, and when that happens, all your bit patterns appear in the second edit window. There could be a shortcut 'paste' button to copy the clipboard into the first window and trigger the algorithm. Another button could be 'copy' to copy the result window into the clipboard.
  • TonyB_TonyB_ Posts: 2,108
    edited 2020-01-22 14:57
    cgracey wrote: »
    Maybe to start a pattern:
      ~ + + + + + + + + + +
    

    To end a pattern:
      ~ - - - - - - - - - -
    

    That could set up the registration of columns and the number of different skip patterns.

    Then, look for either spaces, pipes, or letters in those columns:

    SPACE = ignore
    letter = '0' bit
    | = '1' bit

    Anything other than a SPACE in an unused column within the skip pattern area could be flagged as an error.

    You could make your app such that you paste a block of code into and it produces lines in a text window:

    a %011010
    b %100011
    c %011101

    ...which can be selectively copied to the clipboard for pasting into your program where needed.

    No need for the '~ - - - - - - - - - -' if you're only pasting a block at a time.

    Some thoughts:

    1. ~ at the start of every skip pattern comment is not necessary.

    2. To start a pattern, use '~patt<separator> before skip code lines, such as ~patt_ or ~patt- or ~patt. where patt is a unique base name for next group of patterns to which a or b etc. are appended after the optional separator for each individual skip pattern.

    3. To end a pattern, use '~ after skip code lines.

    4. Spaces should be optional between letters, to reduce line length. Skip letter sequences could be terminated by tabs or double spaces, but only if that is really needed.

    5. As well as space, letters and |, _ should be a valid character, perhaps optional, to cater for situation where some letter patterns start after others. _ represented by _ in the generated skip patterns so that skip bits are aligned consistently. Examine possibility of using more than a-z as skip chars.

    6. A blank line between skip code lines should be permitted for readability and could also be represented by _ in the generated skip patterns.

    Example:

    N.B.
    Spaces between letters, | and _ are for clarity and not compulsory
    No space allowed between ' and ~ for skip pattern start/end
    CON
    patt_a	= %...		'software-generated skip patterns
    patt_b	= %...
    patt_c	= %...
    	...
    			'~patt_			start skip patterns patt_?
    	{code}		'a _ c d e _ _ _ _ _
    	{code}		'a b | | | f g _ _ _
    			'			_ inserted in skip pattern
    	{code}		'| | c | e | | h i j
    			'~			end current skip patterns
    	...		
    execf_a	long	addr_a | patt_a << 10
    execf_b	long	addr_b | patt_b << 10
    execf_c	long	addr_c | patt_c << 10
    	...
    
  • TonyB_TonyB_ Posts: 2,108
    edited 2020-01-22 13:49
    Amended point 2 above to make it more general, by allowing the user to choose the separator (which could be nothing).
  • Cluso99Cluso99 Posts: 18,066
    edited 2020-01-22 14:14
    Based on Chip’s suggestion of cut/paste a single block of code at a time...

    No need for starting or ending sequence.
    No need for the special comment sequence ‘~<space>

    Requirements...
    Table starts after the comment quote character (currently some comments appear before the table)
    No comments can be in the table space (ie vertically)
    Optional single space maximum between table characters
    Minimum 2 spaces or tab or <cr> at the end of the table before comments
    Think I will allow a table to be 52 chars wide meaning up to 52 tables if no spaces used.
    A table item does not need to start on the first instruction (as Chip has done), nor go to the end - just use space instead of the letter or pipe.

    I think I will flag an error if > 22 entries but will still output the table.

    I have the basics for this working now (building the table)

    Currently, in PropTool, I copy the code into a new window and save as “skip_input.spin2” (no need to close PropTool)
    Run my python program which creates the file “skip_output.spin2”
    In PropTool open the “skip_output.spin2” file where the generated code can be copied back into the source file.

    This saves me having to generate windows and cut/paste in python. I have no idea how easy this can be done??
  • TonyB_TonyB_ Posts: 2,108
    edited 2020-01-22 14:32
    Cluso99 wrote: »
    Based on Chip’s suggestion of cut/paste a single block of code at a time...

    No need for starting or ending sequence.
    No need for the special comment sequence ‘~<space>

    A single block at a time would become tedious quickly. Creating skip patterns should be as automatic as possible, e.g. have option to press one key to do the whole lot in one go, which is why I suggested pattern block names. The EXECF tables are likely to be nowhere near the corresponding code.
  • This sort of thing MUST be a compiler feature. Generating magic constants with an external program has all the same conceptual issues as doing it manually, it's just quicker.
  • cgraceycgracey Posts: 14,133
    edited 2020-01-22 15:00
    In my code, I sometimes have skip patterns that span multiple routines. Also, there could be jumps that are taken. It quickly gets too complex for a general compiler solution.

    I would be pretty happy with just a little helper tool. In my case, though, I am very carefully coding things and not casually changing skip patterns around.
  • Wuerfel_21Wuerfel_21 Posts: 4,374
    edited 2020-01-22 15:19
    Following branches and subroutine calls seems entirely doable to me. If the code asks for "skip pattern X starting from address Y", just step through the instructions starting from that address, following all possible code paths (since SKIP is limited to 32 steps, this isn't even _that_ slow) and record the skip bits for each (and error if there's paths that don't line up). This doesn't deal with dynamic jumps, but a list of targets to consider could be given. (Or just fall back to magic constants for such cases) Truth be told, I wouldn't want to be the one implementing it in x86 ASM.

    My Ruby-based assembler, if it was for P2, would eat such a feature for breakfast, although the syntax would likely also be incredibly ugly (as it would be for P2ASM in general. WRLONG x,PTRA++ would have to be WRLONG x,PTRA.postinc or something terrible to that extent...).
  • TonyB_TonyB_ Posts: 2,108
    edited 2020-01-22 15:31
    cgracey wrote: »
    In my code, I sometimes have skip patterns that span multiple routines. Also, there could be jumps that are taken. It quickly gets too complex for a general compiler solution.

    I would be pretty happy with just a little helper tool. In my case, though, I am very carefully coding things and not casually changing skip patterns around.

    I think it gets too complex to do manually and error-free more quickly. Even with fairly simple code, there could be tens of skipping sequences and hundreds of EXECF table entries. The time savings with auto-generation could be immense.

    Jumps could be handled by copying jumped-to code and inserting ' as first char and this extra ' could be ignored by the pattern generator, which should be intelligent enough to cope with most cases.
  • Cluso99Cluso99 Posts: 18,066
    Hey guys,
    I've spent some considerable time with execf writing both the old P1 interpreter and a Z80 implementation. It's a real PITA to calculate the skip mask and that's why I am doing this. Chip has found the same thing. This execf is an absolutely fantastic feature for P2, but is likely only to be used only by a few.

    However, it's a real chore to do it automatically in software and it's easy to mess things up and the compiler would give wrong results easily without even an error.

    So, for now, it WILL be a matter of giving my python program the lines required for the conversion. Once that's complete, you can ask for extensions, but for now that's what I am doing. We need the basics now without any frills.

    BTW I have a more than full-time day job so my time is limited.
  • Cluso99Cluso99 Posts: 18,066
    edited 2020-01-23 12:30
    Calculate skip masks
    First pass version working :)

    It is a python program so you will need python installed. I use 3.7.3 32bit although x64 should also work just fine. There's nothing special about this program.

    The program takes an input file which you must create from your spin2 source (called "skip_input.spin2). I used PropTool to do this. The input must be a singular sequence of skip instructions and it will create the re-hashed input marked as comments (note the instruction gets mangled and truncated - its' just there for reference) followed by the skip masks which can be copied back into your source program. This output file is "skip_output.spin2".

    When next I get time I'll take a look at extracting all the skip sections from a file and create multiple output mask sections.

    Here is a sample of the output. It is designed to reside in a CON section and the jump table will just do:
    xxx long label_skip_a
    ''----------------------------------------------------------------------------------
    ''mod_iso               mov                             '~ a b         g   i   k l      a: ++var, var++ (isolated)                      iso    
    ''mod_psh               pusha                           '~ | | c d e f | h | j | | m    b: --var, var--    (isolated)                      push    
    ''                      alti    rd                      '~ a b c d e f g h i j k l m    c: ++var    (push)                              rd    
    ''rd_field              call                            '~ a b c d e f g h i j k l m    d: --var        (push)     (pipeline spacer)       rd    
    ''                      xoro32    x                     '~ | | | | | | | | | | | l m    e: var++    (push)                                ??    
    ''                      mov    v,x                      '~ | | | | e f | h | j k l m    f: var--    (push)                              post    
    ''                      add    x,#1                     '~ a | c | e | | | | | | | |    g: var!!    (isolated)                          ++    
    ''                      sub    x,#1                     '~ | b | d | f | | | | | | |    h: var!!    (push)                              --    
    ''                      zerox    x,sz                   '~ | | c d | | | | | | | | |    i: var!        (isolated)                          ptr    
    ''                      muxz    x,_FFFFFFFF             '~ | | | | | | g h | | | | |    j: var!        (push)                              !!
    ''                      not    x                        '~ | | | | | | | | i j | | |    k: var\new    (swap)                              !    
    ''                      mov    x,w                      '~ | | | | | | | | | | k | |    l: ??var    (isolated)                          swap    
    ''                      alti    wr                      '~ a b c d e f g h i j k l m    m: ??var    (push)                              wr    
    ''wr_field              call                            '~ a b c d e f g h i j k l m                               (pipeline spacer)       wr    
    ''                      _ret_    mov    x,w             '~ a b | | | | g | i | | l |                                                    iso    
    ''                      _ret_    mov    x,v             '~     | | e f   h   j k   m                                                    iso    
    ''                      ret                             '~     c d                                                                      main    
    ''----------------------------------------------------------------------------------
    mod_iso_skip_a          = mod_iso         |        %000111110110010 << 10  '
    mod_iso_skip_b          = mod_iso         |        %000111101110010 << 10  '
    mod_psh_skip_c          = mod_psh         |       %0110011101011000 << 10  '
    mod_psh_skip_d          = mod_psh         |       %0110011100111000 << 10  '
    mod_psh_skip_e          = mod_psh         |        %010011111001000 << 10  '
    mod_psh_skip_f          = mod_psh         |        %010011110101000 << 10  '
    mod_iso_skip_g          = mod_iso         |        %000110111110010 << 10  '
    mod_psh_skip_h          = mod_psh         |        %010011011101000 << 10  '
    mod_iso_skip_i          = mod_iso         |        %000101111110010 << 10  '
    mod_psh_skip_j          = mod_psh         |        %010010111101000 << 10  '
    mod_iso_skip_k          = mod_iso         |       %0100011111010010 << 10  '
    mod_iso_skip_l          = mod_iso         |        %000111111000010 << 10  '
    mod_psh_skip_m          = mod_psh         |        %010011111100000 << 10  '
    ''----------------------------------------------------------------------------------
    
  • cgraceycgracey Posts: 14,133
    Very nice, Cluso99. I suppose it errors out if a sequence starts where there's no label?
  • Cluso99Cluso99 Posts: 18,066
    edited 2020-01-23 13:27
    Yes, or if there is a skip sequence and no instruction although the test is pretty basic.

    Just been looking thru your interpreter code and there are a few gotchas.

    There are a few places where comments appear before the mask code eg line with label mod_iso
    mod_iso mov w,x 'iso a b g i k l a: ++var, var++ (isolated)

    Noticed a jump within the sequence. Not sure how to do this one. eg in the callh routine the jmp #makeptr

    Lines where the table is has not been cleared before comments start. eg calloffh routine a few lies below has x2 under the i j columns

    Commented code using { } within table code eg at callobj routine

    Any chance you can repost your code with spaces instead of tabs? Its playing havoc in PropTool.
  • Cluso99Cluso99 Posts: 18,066
    I am trying just inserting this line before and after a table sequence
    '~------------------------------------------------------------------------------
    

    With the beginning and end marked, I will not need each comment/table to begin with '~<space>
  • cgraceycgracey Posts: 14,133
    I will repost it shortly. Good job getting rid of the need for the "~" on each line. Better to have one header line at the top.
  • cgraceycgracey Posts: 14,133
    Cluso99, here is the current interpreter code with spaces and no tabs.

  • Is the "skip value" encoded in the byte code? It's a little hard to read at first, but what I realized is that you write 1 sub for a group of like opcodes, then skip a certain number of instructions. like "call op_something+skip".
  • Cluso99Cluso99 Posts: 18,066
    edited 2020-01-23 19:38
    See the docs write up under execf, skip and skipf.

    Basically you create a jump table of longs where the longs are made up of a 10 bit cog /LUT address of where to jump to and a 22 bit skip field that gets loaded into the internal skip register. A “1” means skip the instruction and a “0” means execute the instruction.
    The jump table is a bytecode offset so when the “exec” instruction executes it fetches the appropriate entry from the jump table according to the bytecode.
    Execution then continues from the cog/LUT address given in the jump table with the skip bits loaded in the internal skip register. As execution continues, the skip register is shifted right. If the rightmost bit is a “1” the instruction is skipped. As an added bonus, skipping up to 7 instructions in a row takes “no” clocks providing it’s a cog exec/LUT exec instruction.. And the execf instruction takes a total of 6 clocks IIRC.

    This latter discussion has been about automatically creating the jump table and specifically the skip mask.

    It’s a fantastic mechanism for bytecode interpreters and is extremely fast !!!!!
  • Some might even say it's novel enough to be patented....
  • Cluso99Cluso99 Posts: 18,066
    pedward wrote: »
    Some might even say it's novel enough to be patented....
    If it was any other company it would be!
    But you must have deep pockets to enforce it, otherwise the big guys just steal it anyway, so what's the point :(
    BTW Interesting to see MS, Apple & IBM rallying legal support for Google vs Oracle and API copyright :wink:
  • cgraceycgracey Posts: 14,133
    > @pedward said:
    > Some might even say it's novel enough to be patented....

    Pedward, that's a garbage-can word. You know we don't use words like that here.
  • pedwardpedward Posts: 1,642
    edited 2020-01-24 18:57
    It was tongue in cheek, you already let the genie out of the bottle, so it can't be patented...

    You could write a paper about it and call it "SkipMap". It's pretty interesting in its own right.
Sign In or Register to comment.