Question about ALTDS implementation in new chip — Parallax Forums

# Question about ALTDS implementation in new chip

Posts: 2,765
edited 2014-06-29 09:42
Chip
Did these changes to ALTDS make it into the new chip?

See here

Cheers
Brian
«1

• Posts: 13,610
edited 2014-06-26 16:46
ALTDS uses a D register for D/S field substitutions in the next instruction, while S/# modifies the D register's D and S fields and controls D/S substitution.

ALTDS D,S/#

D - a register whose D/S fields may be substituted for the next instructions' D/S fields
S/# - an 8-bit code: %ABBBCDDD

%A: 0 = don't substitute next instructions' D field with current D register's D field, 1 = substitute next instructions' D field with current D register's D field
%BBB: 000 = leave the current D register's D field the same, 0xx = add 1/2/3 to D field, 1xx = subtract 1/2/3/4 from D field
%C: 0 = don't substitute next instructions' S field with current D register's S field, 1 = substitute next instructions' S field with current D register's S field
%DDD: 000 = leave the current D register's S field the same, 0xx = add 1/2/3 to S field, 1xx = subtract 1/2/3/4 from S field
• Posts: 17,833
edited 2014-06-26 22:29
Chip,
In the linked post you discussed this...
cgracey wrote: »
Great idea!!!

We've only got six bits specified for S/# in ALTDS, so we can use the three bits above to specify write-register alteration, with D[31:23] serving as the pointer for write redirection:

ALTDS D,S/#

S/# = %rrr_ddd_sss

rrr = same as ddd/sss, but uses D[31:23] as a write redirection pointer.

MOVI D,S/# can be used to set D[31:23]

Now we've got it all in one instruction!!!
This permits the additional possibilities of:
* redirecting the result
* redirecting the result to an unused register (maybe INx) to perform a pseudo NR

Therefore, might it be beneficial, and would it be easy to do the following ???
S/# = %RRRDDDSSS
where RRR, DDD and SSS mean:
000 = don't substitute next instructions S/D/R field, leave the current D registers S/D/I value the same
001 = substitute next instructions S/D/R field with the current D registers S/D/I field, then add 1 to the current D registers S/D/I value
010 = substitute next instructions S/D/R field with the current D registers S/D/I field, then add 2 to the current D registers S/D/I value
011 = substitute next instructions S/D/R field with the current D registers S/D/I field, then add 4 to the current D registers S/D/I value
100 = substitute next instructions S/D/R field with the current D registers S/D/I field, leave the current D registers S/D/I value the same
101 = substitute next instructions S/D/R field with the current D registers S/D/I field, then subtract 1 from the current D registers S/D/I value
110 = substitute next instructions S/D/R field with the current D registers S/D/I field, then subtract 2 from the current D registers S/D/I value
111 = substitute next instructions S/D/R field with the current D registers S/D/I field, then subtract 4 from the current D registers S/D/I value

1/2/4 covers byte/word/long in hub, and 1/2/4 longs in cog.
• Posts: 13,610
edited 2014-06-27 04:39
Cluso99 wrote: »
Chip,
In the linked post you discussed this...

This permits the additional possibilities of:
* redirecting the result
* redirecting the result to an unused register (maybe INx) to perform a pseudo NR

Therefore, might it be beneficial, and would it be easy to do the following ???
S/# = %RRRDDDSSS
where RRR, DDD and SSS mean:
000 = don't substitute next instructions S/D/R field, leave the current D registers S/D/I value the same
001 = substitute next instructions S/D/R field with the current D registers S/D/I field, then add 1 to the current D registers S/D/I value
010 = substitute next instructions S/D/R field with the current D registers S/D/I field, then add 2 to the current D registers S/D/I value
011 = substitute next instructions S/D/R field with the current D registers S/D/I field, then add 4 to the current D registers S/D/I value
100 = substitute next instructions S/D/R field with the current D registers S/D/I field, leave the current D registers S/D/I value the same
101 = substitute next instructions S/D/R field with the current D registers S/D/I field, then subtract 1 from the current D registers S/D/I value
110 = substitute next instructions S/D/R field with the current D registers S/D/I field, then subtract 2 from the current D registers S/D/I value
111 = substitute next instructions S/D/R field with the current D registers S/D/I field, then subtract 4 from the current D registers S/D/I value

1/2/4 covers byte/word/long in hub, and 1/2/4 longs in cog.

I had forgotten about this! Yes, we'll have three 3-bit fields so that the result can be redirected, as well. Thanks for bringing this up.
• Posts: 17,833
edited 2014-06-27 05:49
cgracey wrote: »
I had forgotten about this! Yes, we'll have three 3-bit fields so that the result can be redirected, as well. Thanks for bringing this up.
Fantastic, thanks Chip
• Posts: 17,833
edited 2014-06-27 13:29
Chip,
Would it be easy to add a special case of S=000_000_000 (effectively did nothing) to cancel the write back of the result?
This could have its own mnemonic "NR" meaning the next instruction would be equivalent to an NR effect. This makes up for the lost NR effect in the P1. So by using a pair of instructions we could set the Z and C flags without changing the destination.
• Posts: 13,610
edited 2014-06-27 17:55
Cluso99 wrote: »
Chip,
Would it be easy to add a special case of S=000_000_000 (effectively did nothing) to cancel the write back of the result?
This could have its own mnemonic "NR" meaning the next instruction would be equivalent to an NR effect. This makes up for the lost NR effect in the P1. So by using a pair of instructions we could set the Z and C flags without changing the destination.

Good idea! I'll make it do that.
• Posts: 2,765
edited 2014-06-27 18:32
Great Chip!
A versatile replacement for the indirect registers of the old P2.
• Posts: 17,833
edited 2014-06-27 23:32
Fantastic news Chip!

How are things progressing?
• Posts: 6,445
edited 2014-06-29 07:16
Good idea!

Could the same effect be achieved by redirecting the D to INA? (ie the hidden ram behind it)
Cluso99 wrote: »
Chip,
Would it be easy to add a special case of S=000_000_000 (effectively did nothing) to cancel the write back of the result?
This could have its own mnemonic "NR" meaning the next instruction would be equivalent to an NR effect. This makes up for the lost NR effect in the P1. So by using a pair of instructions we could set the Z and C flags without changing the destination.
• Posts: 13,610
edited 2014-06-29 07:28
Good idea!

Could the same effect be achieved by redirecting the D to INA? (ie the hidden ram behind it)

Yes, but you would need a register to be holding \$1FA<<23. By using S=0 to do the same thing, A simple ALTDS 0,#0 could cancel the write.
• Posts: 13,610
edited 2014-06-29 07:30
Cluso99 wrote: »
Fantastic news Chip!

How are things progressing?

Really solidly and simply. This new design has no hard-to-think-about details, so it's not difficult to know when sections of it are completed, which is a big stress reliever.
• Posts: 6,445
edited 2014-06-29 07:43
Makes sense, thanks.

cgracey wrote: »
Yes, but you would need a register to be holding \$1FA<<23. By using S=0 to do the same thing, A simple ALTDS 0,#0 could cancel the write.
• Posts: 17,833
edited 2014-06-29 09:42
cgracey wrote: »
Really solidly and simply. This new design has no hard-to-think-about details, so it's not difficult to know when sections of it are completed, which is a big stress reliever.
That's great news. Simple designs are also simpler to explain and understand too.
• Posts: 2,474
@cgracey: Is the current ALTDS implementation the same as documented here? If so, I have a few questions:

What is the value of altering both the D and S at the same time?

What is the value of altering the instruction bits?

What would happen if you alter S on an instruction like COGID (where S is part of the opcode)?

How would I go about moving data between two buffers? For instance:
```                mov count, #16
:loop           altds ????, #%000_001_001
mov 0, 0
djnz count, #:loop

dest            long    \$100
src             long    \$180
count           res     1
```
• Posts: 23,031
I think it would be:
```          mov   count, #16
mov   pntrs, initial
:loop     altds pntrs, #%000_001_001
mov   0, 0
djnz  count, #:loop

initial   long   \$100<< 9 | \$180
pntrs     res    1
count     res    1
```
• Posts: 2,474
Mike Green wrote: »
I think it would be:
```          mov   count, #16
mov   pntrs, initial
:loop     altds pntrs, #%000_001_001
mov   0, 0
djnz  count, #:loop

initial   long   \$100<< 9 | \$180
pntrs     res    1
count     res    1
```

I was wondering if that was the approach. If so, this would imply that just to manipulate D, you would always have to start with a 9-bit value that's shifted 9 bits.

Actually, the more I think about this, that's the only way to do this. ALTDS has to be able to write back to a single register (unless it's going to take additional clock cycles), which would mean that both D and S values would have to be in the same register.

Doing a slight variation of your solution, maybe something like:
```                setd indirect, dest
sets indirect, src
mov count, #16
:loop           altds indirect, #%000_001_001
mov 0, 0
djnz count, #:loop

dest            long    \$100
src             long    \$180
indirect        res     1
count           res     1
```

• Posts: 1,440
Can you chain multiple ALTDS instructions to obtain more levels of indirection?
• Posts: 23,031
No

You could have an ALTDS followed by another ALTDS, but the first one would affect the instruction following it which is another ALTDS and modify the destination and source addresses/operands of the second ALTDS. The modified second ALTDS would, in turn, modify the instruction following it, but that doesn't give you multiple levels of indirection.
• Posts: 1,440
edited 2015-09-13 03:55
Mike Green wrote: »
No

You could have an ALTDS followed by another ALTDS, but the first one would affect the instruction following it which is another ALTDS and modify the destination and source addresses/operands of the second ALTDS. The modified second ALTDS would, in turn, modify the instruction following it, but that doesn't give you multiple levels of indirection.

That sounds to me like multiple levels of indirection. What am I missing?

From what I can tell, the following should copy the register pointed to by the register pointed to by ptr2ptr into result (result = **ptr2ptr):
```altds ptr2ptr, #%000_000_100
altds 0-0, #%000_000_100
mov result, 0-0
```

EDIT: the above is wrong; the following is modified to replace the D instead of S field of the seconds ALTDS:
```altds ptr2ptr, #%000_100_000
altds 0-0, #%000_000_100
mov result, 0-0
```
• Posts: 2,474
edited 2015-09-13 03:23
Mike Green wrote: »
No

You could have an ALTDS followed by another ALTDS, but the first one would affect the instruction following it which is another ALTDS and modify the destination and source addresses/operands of the second ALTDS. The modified second ALTDS would, in turn, modify the instruction following it, but that doesn't give you multiple levels of indirection.

That sounds to me like multiple levels of indirection. What am I missing?

From what I can tell, the following should copy the register pointed to by the register pointed to by ptr2ptr into result (result = **ptr2ptr):
```altds ptr2ptr, #%000_000_100
altds 0-0, #%000_000_100
mov result, 0-0
```

The first ALTDS would replace the "%000_000_100" in the second ALTDS. But you actually need to replace the d-field. Maybe something like this:
```MOV reg2, #\$100              ' pretend reg2 is at \$0FF
MOV reg1, #reg2
SHL reg1, #9                 ' reg1 contains %01_11111110_00000000
ALTDS addr1, #000_001_000    ' replaces next instruction's D with \$0FF (which contains \$100)
ALTDS 0-0, #000_000_001      ' replaces next instruction's S with \$100
MOV dest, 0-0                 ' becomes MOV dest, \$100
```
• Posts: 2,474
edited 2015-09-13 03:22
But, more to Mike's point, I think he's saying that the you cannot do double indirection simply be using two consecutive ALTDSs. You must manipulate the indirect pointers to get them to work right.
• Posts: 2,474
edited 2015-09-13 03:54
Suppose you just got rid of the increment/decrement feature, as well as the third field manipulation, and did the following:
```0111000 00- CCCC --------- ---------		NR            ' for Cluso
0111000 01I CCCC --------- SSSSSSSSS		ALTS	S/#
0111000 10I CCCC DDDDDDDDD ---------		ALTD	D/#
0111000 11I CCCC DDDDDDDDD SSSSSSSSS		ALTDS	D,S/#
```

Yes, you would have to manually increment/decrement the indirect registers. But this approach would be so much simpler to understand and would not require bit shifting, configuration bits, etc. It would also let @Electrodude do double indirection.
• Posts: 1,440
edited 2015-09-13 04:20
```mov   tmp,    ptr2ptr       ' tmp = ptr2ptr
shl   tmp,    #9            ' shift ptr2ptr into D field of tmp
altds tmp,    #%000_100_000 ' use D field of tmp as D field of next instruction
altds 0-0,    #%000_000_100 ' use S field of register pointed to by ptr2ptr (D of tmp)
'  as S field of next instruction
mov   result, 0-0           ' result = **(D of tmp) = **ptr2ptr
```
• Posts: 11,258
edited 2015-09-13 04:47
Just out of interest, I think the following will work for single indirection:
```               setd indirect, #\$100
sets indirect, #\$180
mov count, #16
:loop          altds indirect, #%000_001_001
indirect       mov 0-0, 0-0
djnz count, #:loop

count          res     1
```
The ALTDS instruction will write back after the instruction fetch of MOV so the result is still the same as a separately stored Cog pointer D/S pair.
• Posts: 11,258
edited 2015-09-13 06:10
That should work nicely for RD/WRLUT as well. Running tables is the common use.

Interestingly, ALTDS only saves one instruction in the loop. REP saves another though, so it becomes something like:
```               setd indirect, #\$100
sets indirect, #\$180
rep #(loopend-loop), #16
:loop          altds indirect, #%000_001_001
indirect       mov 0-0, 0-0
:loopend
```

PS: And the only reason ALTDS saves an instruction here is because both the source and destination are in the same MOV instruction. If the operation is more complicated than a single instruction then an ADD srcptr / ADD destptr instruction pair will still be as quick.

But it does save space still, by not needing a separate pointer store, so there is that.

PPS: To be fair, this is not full indirection but rather a limited but common use case in Cog space. Won't most full indirection be in Hub space?
• Posts: 11,258
Oh, an ALTDS preceding an AUGD/AUGS could be fun. Would be nice to think that the three fields can fill the entire 23 immediate bits of the AUG instructions. But I guess the PTRA/PTRB registers are intended to fill this roll.
• Posts: 11,258
edited 2015-09-13 07:12
Seairth wrote: »
The first ALTDS would replace the "%000_000_100" in the second ALTDS. But you actually need to replace the d-field. Maybe something like this:
```MOV reg2, #\$100              ' pretend reg2 is at \$0FF
MOV reg1, #reg2
SHL reg1, #9                 ' reg1 contains %01_11111110_00000000
ALTDS addr1, #000_001_000    ' replaces next instruction's D with \$0FF (which contains \$100)
ALTDS 0-0, #000_000_001      ' replaces next instruction's S with \$100
MOV dest, 0-0                 ' becomes MOV dest, \$100
```

A tidy up would be:
```                SETD reg1, #reg2            ' Cog pointer to Cog pointer preassigned, \$FF placed in D operand of the ALTDS below.  If traversing a table of Cog/LUT pointers then #reg2 wouldn't be an immediate operand.
NOP                         ' Dummy for SETD to write back before ALTDS is fetched.
reg1            ALTDS 0-0, #000_000_100     ' replaces next instruction's S from the assigned, in this case with \$100
MOV dest, 0-0               ' becomes MOV dest, \$100

reg2            LONG #\$100
```

That's neat. I wasn't expecting this result of only a single ALTDS needed. Maybe my previous use wasn't so narrow after all.
• Posts: 11,258
Electrodude,
I think you've missed a little detail. The assignment of ptr2ptr into tmp fails because you are copying the contents of ptr2ptr rather than it's address.

And ALTDS is only capable of working with contents as it's source and writeback. That's why I've used the SETD instruction - to extract the address of reg2.
• Posts: 11,258
edited 2015-09-13 09:41
Interestingly, a real example of double indirection seems to not only need two ALTDS's but also the intermediary MOV as well. Also note I couldn't use self-modifying code for storing the pointers because they are applied as S operands. EDIT: All the earlier attempts, mine included, fail because of this need to work on the S operand.
```                mov  total, #0
mov  ptr2ptr, #raceptrtable
rep  #(loopend-loop), #(racetablend-raceptrtable)
:loop
altds  ptr2ptr, #000_000_001   ' apply ptr2ptr to next instruction then increment
mov  ptr1, 0-0                 ' use the applied pointer
altds  ptr1, #000_000_100      ' apply ptr1 to next instruction
add  total, 0-0                ' sum the list scores  - ie: use the applied pointer
:loopend

total           res  1
ptr1            res  1
ptr2ptr         res  1
raceptrtable    long  \$100,\$10A,\$11A,\$12C,\$138,\$144   ' table of pointers to list of scores
racetablend
```
• Posts: 11,258
edited 2015-09-13 11:15
Err, I could save a long and have self modifying by changing the intermediate MOV to store into itself.
```                mov  total, #0
mov  ptr2ptr, #raceptrtable
rep  #(:loopend-:loop), #(racetablend-raceptrtable)
:loop
altds  ptr2ptr, #000_000_001   ' apply ptr2ptr to next instruction then increment
ptr1            sets  ptr1, 0-0                ' store a table entry right in this instruction
altds  ptr1, #000_000_100      ' apply ptr1 to next instruction
add  total, 0-0                ' sum the list scores  - ie: use the applied pointer
:loopend

total           res  1
ptr2ptr         res  1
raceptrtable    long  \$100,\$10A,\$11A,\$12C,\$138,\$144   ' table of pointers to list of scores
racetablend
```