(pretty much a copy of my post on maxcoderz but w/e)

I've been working on threading on the z80 a bit, and I don't really like hacking around with di/ei all the time. It's especially annoying when you don't know whether interrupts are disabled already or not and you want to nice and leave them disabled.
So I needed some atomic stuff to implement locks and the like (which are also hugely useful by themselves in a multithreaded context)

I have this one:

Code:
testandset:
   ld hl,tas_thingy
   scf
   jp (hl)
tas_thingy:
   res 1,(hl) ; changes itself to ret (res = 11001011)
   or a
   ret

I suppose I use "ld (hl),$C9" instead but whatever.

It's nice for locks, but locks have "the lock problem" and otherwise TaS is a pretty weak routine with a consensus number of just 2.

The main question: Are more powerful atomic constructs possible on the z80?

In the mean time, here are some more "Test and Set"-like things.


Code:
rlc (hl)
ret
ret
; changes itself into
sub a
ld b,$C9
ret

Pro: puts the result in both the C and Z flag.
Con: kills B (could also load B with zero)


Code:
rrc (hl)
ret
ret
; changes itself into
push hl
ld c,$C9
ret

Pro: auto-loops while failed, so this really implements a binary semaphore instead of TaS. I also did some counting semaphores which you can see in the maxcoderz thread or I';; copy them over here if anyone wants that.
Con: kills C (could also load C with zero)


Code:
sla (hl)
ret
; changes itself into
sub (hl)
ld h,(hl)
ret

Pro: shortest (3 bytes vs 4 for the others) TaS sequence I know of
Con: kills H - but that's less annoying than killing B or C


Code:
rlc b,(ix+%001xx000)
; becomes
sub a
jr xx,0

You can change the xx bits to whatever, the jump has an offset of zero anyway so it's irrelevant.
The result is in both Z and C flags.
Downsides: kills A and B, and is pretty big (5 bytes if a ret is included)

But the pattern can be more useful than that. You know the condition is going to be Z+NC at that jump, and you can use any offset up to and including 7 (with 6 being a special nice case in that it isn't an autocopy and so doesn't destroy regs and 7 autocopying to A which is killed anyway - though in a different way)
You can use this to skip whatever is done after the rlc (up to 7 bytes anyway) and do whatever you want (such as, for example, marking the thread as Waiting)
Nice to see you on Cemetech, harold! I just now found a way to do test-and-set without self-modifying code:


Code:
#define LOCKED $FF
#define UNLOCKED $FE

;Test-and-set
;Returns C if was locked, NC if was unlocked
    ld hl,tas_var
    sra (hl)

;Reset
    ld hl,tas_var
    ld (hl),UNLOCKED


Alternatively, in the Reset routine you can optimize to dec (hl) if you are absolutely certain that the variable is LOCKED. Also, if you are able to put these variables relative to a constant index register (like IY in TI-OS), these operations become even smaller (and don't destroy HL)
Very nice! I missed that one completely lol
It would work with SLL too then I suppose (but that's not better in any way and it uses an undocumented instruction)
harold wrote:
Very nice! I missed that one completely 0x5
It would work with SLL too then I suppose (but that's not better in any way and it uses an undocumented instruction)
Yup, which means that it wouldn't work on the Nspire, that purple elephant of calculators. Sad I actually had this topic sitting open in a tab in my browser all day, waiting for me to type a response out. I'm quite impressed with the number of TAS implementations that you came up with. If you decide to examine a conditional-interrupt-disable mechanism, we've come up with a pretty foolproof way to enter a section of critical code and only turn interrupts back on afterwards if they were already on, which I use in several critical sections of Doors CS.
The ld a, i instruction lets you know whether interrupts are enabled via the P/V flag. So you can save the interrupt status, di, do stuff, and then decide whether or not to ei.
DrDnar wrote:
The ld a, i instruction lets you know whether interrupts are enabled via the P/V flag. So you can save the interrupt status, di, do stuff, and then decide whether or not to ei.
Yes. However, there are a couple of glitches with that when interrupts trigger on that instruction, and they happen very rarely, but when you're running an interrupt over one hundred times a second, then the glitch shows up in a minute or less. Here's the solution, a compilation of work from myself, Quigibo, and Calc84Maniac, proven correct on Nspires and 83+s through 84+SEs.


Code:
iFastCopy:
   call iCheckInts0
   push af
      di
      ;....critical section...
      pop af
   ret po
   ei
   ret

iCheckInts0:
;Push a zero byte to the stack and pop it
   xor a
   push af
      pop af
   ld a,i
   ret pe
   ;See if an interrupt triggered. If so, the byte on the stack will not be 0 anymore
   dec sp
   dec sp
   pop af
   add a,a
   ret z
   xor a
   ret
Glitches? Well, that could explain some of USB8X's bugs.
DrDnar wrote:
Glitches? Well, that could explain some of USB8X's bugs.
Oh really? Yeah, it's mainly just that the flags get butchered if the interrupt happens on that instruction. The code I posted there works wonders for things like iFastCopy and iLargeSprite working together with CALCnet.
Why is that? It sounds like deep Z80 magic.
If I'm not mistaken, the "ld a,i " has to be "dealigned" otherwise the low byte of the address could still be zero, right?
That's not such a bad thing though, just something to keep in mind..
It's very good, I may have to borrow that one as well, if you don't mind Smile
Indeed, and I periodically check that recompiling Doors CS hasn't fulfilled the 1/256 chance that it is indeed aligned. Smile You're welcome to give that a try if you find that you need it, and suggest any optimizations you may notice, although I couldn't find any. DrDnar, it is indeed. The outcome of the glitch is of course that interrupts appear disabled (parity flag odd) when they are in fact enabled (pe), leading the routine in question to leave the interrupt disabled unless this trick is used.
This belongs on WikiTI.
harold wrote:
It would work with SLL too then I suppose

And sla and srl too of course, with inverted logic, with the small benefit that the result is in the Z flag aswell as the C flag.
DrDnar wrote:
This belongs on WikiTI.
I shall get on it momentarily, good idea. Smile
harold wrote:
If I'm not mistaken, the "ld a,i " has to be "dealigned" otherwise the low byte of the address could still be zero, right?
That's not such a bad thing though, just something to keep in mind..
It's very good, I may have to borrow that one as well, if you don't mind Smile

Actually, it checks the *high* byte of the address. So there is a problem only if you are executing this routine from $0000-$00FF range (which is only likely to happen if you're running a custom OS)
Oh duh, good point, I'm ridiculously dumb. Smile Thanks for pointing that out, calc84.
Right yes of course, A is the high byte of AF after all..

Anyway, does anyone have an answer to my original question? Are more powerful atomic constructs possible on the z80?
harold wrote:
Right yes of course, A is the high byte of AF after all..

Anyway, does anyone have an answer to my original question? Are more powerful atomic constructs possible on the z80?
Well, once you have atomic critical sections, you can build spinlocks and mutices (mutexes?) from there. Or do you mean without building off of the atomic code?
Without cheating, of course Smile
wait-free atomics, or else you're really just locking and you'd combine the worst of locks and atomics..
harold wrote:
Without cheating, of course Smile
wait-free atomics, or else you're really just locking and you'd combine the worst of locks and atomics..
Well, you can't really create a spinlock or a mutex without having some hardware support (or faked support, in this case) for atomic instructions, as far as I can think and design, no? Am I missing something?
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement