Question about the PINx & DIRx registers and instructions AND, ANDN, OR, XOR behavour
Cluso99
Posts: 18,069
I have been converting an old P1 program that uses AND, ANDN, OR and XOR instructions to modify the I/O pins using the INA and OUTA registers on the P1.
We now have a PINA register that replaces/combines the INA and OUTA registers. From the information available, reading the PINA register reads the level appearing at the pin(s).
What we did in the past when we wanted to change a few pins was to do...
XOR OUTA,#7 'inverts the current P0,1,2 pins.
This instruction reads the output latch of OUTA, exclusive ors the lower 3 bits, and writes back to the output register.
When the P2 performs this using PINA, I presume the output pins will be read (not the output latch), exclusive ors the lower 3 bits, and writes back the result. This could result in an error in writing back the results because an output pin(s) may be overridden by an external force, resulting in incorrect operation. Is this assumption correct? Is there any simple way around this besides keeping a software set of the PINA output register?
I also presume the default map of the PINA/DIRA is to P0..P31 and I do not need to initialise this setting unless I require a different bank of pins on PINA/DIRA. Is this correct?
We now have a PINA register that replaces/combines the INA and OUTA registers. From the information available, reading the PINA register reads the level appearing at the pin(s).
What we did in the past when we wanted to change a few pins was to do...
XOR OUTA,#7 'inverts the current P0,1,2 pins.
This instruction reads the output latch of OUTA, exclusive ors the lower 3 bits, and writes back to the output register.
When the P2 performs this using PINA, I presume the output pins will be read (not the output latch), exclusive ors the lower 3 bits, and writes back the result. This could result in an error in writing back the results because an output pin(s) may be overridden by an external force, resulting in incorrect operation. Is this assumption correct? Is there any simple way around this besides keeping a software set of the PINA output register?
I also presume the default map of the PINA/DIRA is to P0..P31 and I do not need to initialise this setting unless I require a different bank of pins on PINA/DIRA. Is this correct?
Comments
I think the rule is simply this: destination PINA == OUTA, source PINA == INA.
-Phil
I don't recall that discussion. (In the P1, that's only true of certain counter and read-only registers.)
Anyway, quite awhile back, I wrote a script to examine several hundred Spin/PASM programs for INA/OUTA usage and sent the results to Chip. INA, of course, was never used as a destination register and OUTA was never (with maybe one or two exceptions) used as a source register in any of those programs. So it made sense to assign them -- with no loss of functionality -- to a single logical address, PINA, even though they're two separate registers. That notwithstanding, there's no reason to expect that
won't be equivalent to the former
-Phil
Bean
MOV x,PINA 'INA is read
XOR PINA,x 'OUTA is read and written
TEST PINA,x 'INA is read since there is no write
Back-to-back XORs are fine, because they are accessing OUTA, and are not dependent upon INA feedback.
which clearly reads ina, as in the P1.
This is especially vexing as it leaves two ways to test ina but none to test outa.
-Phil
Thanks,
-Phil
[post]1145045[/post]
That would be a good solution to the dilemma, but I don't know if I'd make it mandatory, because I like PINA and it doesn't look like two registers to me. In my experience, so far, PINA works intuitively and I haven't had to stop and think about what was happening.
I guess it helps that you designed it. However, I think the mere existence of this thread speaks to the confusion that others might suffer.
-Phil
Sorry Chip, I must have been thinking of something else....
Bean
That's right.
Perhaps, but it seems to me that people are supposing there's some trap awaiting them, when there really isn't:
- When you are reading PINS, you read the inputs.
- When you are writing PINS, you write the outputs.
- Read-modify-writes to PINS work on the outputs, as this is most useful and probably expected.
(I'm just restating those rules for general consumption on the forum.)
I think the worries are much more theoretical than practical, especially since there are bidirectional clock delays between PINS and the actual I/O pins, which would make a mess of read-modify-write operations that involved both inputs AND outputs. That would cause some major headaches and require a lot of splainin'.
What happens in Spin, then, with these two statements?
Would it not be clearer -- especially in the second case -- to specify which register (ina vs. outa) is being read? (Or is it not possible to read outa?) I'm just afraid that pina's appearance of ambiguity is going to trip up a lot of people.
-Phil
I think you are correct that there isn't a direct way to simply read from OUTA, since source is always INA. As a result, I would think the interpreter must implement those as a single instruction (AND PINA, #7), as it just doesn't make sense to read from INA (one instruction) and then write to OUTA (the next instruction).
So if you need to "read" OUTA, you would need to use another register to hold the current value. I wonder if you could use INDA with the shadow register at the PINA address. But even if you could, it's probably not worth the extra confusion, complexity, etc.
Now, that's a problem!
Not only will we need different names, we'll need to maintain separate variables for the OUTs. This can be hidden in the interpreter, but it makes me wish I had left the INs and OUTs separate, like on Prop1. The problem we can't get around is when you have multitasking code running in the same cog as Spin. Spin can keep proper track of its OUTs, but the concurrent PASM code will not play well with Spin, as it's wont to do operations directly on PINx registers.
Maybe the solution to this is treating Spin assignments like 'PINA &= 7' as atomic operations in the interpreter. That would better prime people for PASM, anyway. Only writes and read-modify-writes would be allowed to affect PINx registers. I think that might be better. What do you think?
I prefer the above, because it's perfectly clear what the programmer's intent is, and the Spin programmer doesn't have to know that ina and outa share the same address -- just that certain usages are invalid.
Here's another pathological example that could be cleared up by using outa:
BTW, it's still not clear to me whether outa can be read directly. If not, the interpreter could always transfer data to it via a dedicated mirror register and never use the read/modify/write form.
-Phil
or in C:
Handling the assignment operator as a special case breaks this.
I like the format above, as it makes it clear what will happen, and also gives any compiler writer, some leverage to check what the user intended.
The objective is to have 'no surprises' in any language, and to make sure the eventual ASM code created does what was intended.
and DJNZ/IJNZ/DJZ/IJZ ?
I see the issue with spin and perhaps other high level languages. If you have to rev the silicon, perhaps a separate new instruction for reading the outx register may be in order just to cover this specific situation.
-Phil
What does INA contain when the pin direction is set to OUT?
-Phil
We could easily establish mirror registers in high-level languages that get copied to PINx, but this leaves on big problem: concurrent, multi-tasking, user-written PASM code that will be able to run along with the Spin interpreter will tend to do operations on PINx directly, blowing up the mirror-register concept. That's why it might be necessary to break PINx up into OUTx and INx within high-level languages so that OUTx operations are always atomic. In other words, OUTx can only appear on the left-hand side of an assigment and INx can only appear on the right-hand side. Then, we have compatibility between multi-tasking PASM code and high-level code that is either interpreted or compiled.
since the value of the parenthesized subexpression already exists on the stack without having to read outa.
-Phil
Addendum: But this would be a problem:
So INA would mirror OUTA for those pins? Would there be a delay from when OUTA is set and INA reflects the new output level? And it would still be possible for an external circuit to override that value (e.g. Pull to a logic zero even though the output is set to a logic one)?
-Phil
And we'd have to apply the same rules for DIRx, in order to keep all operations atomic so that concurrent PASM code runs properly.