Yea I know that one, but as you can see it's a bit of a bother and it's only an option when the code in between is "fast enough" - and you can't transparently wrap any code in this, because you need "special code" to return something in A
There is no "fetch-and-de/increment", but inc and dec are atomic and give just enough information in the flags for a simple (ie breaking on too many releases) unfair semaphore (it's biased by 1, so 1 means "none" and it only goes up to 126):
Code: Select all
Wait:
dec (hl)
-:
jp m,{+}
ret nz
+: ; val is <= 0
; yield
xor a
or (hl)
jr {-}
Release:
inc (hl)
ret
And here is a version of Release that blocks until it wouldn't overflow (only for "single writer" where the one doing the release is seen as the writer):
Code: Select all
Release:
-: inc (hl)
ret po
dec (hl)
; yield
jr {-}
It would temporarily leave the semaphore counter in overflowed state and that would block any Wait operations in the meantime - but that's not actually harmful, just a bit "less than optimal"
You can use a pair of these single-writer multiple-reader semaphores to create a multiple-reader multiple-writer semaphore.
Even simpler (only for single-reader, single-writer):
Code: Select all
wait:
dec (hl)
ret po
inc (hl)
; yield
jr wait
release:
inc (hl)
ret po
dec (hl)
; yield
jr release
It's biased by $80 so it has $80 = zero. And it goes up to 255 (which is represented by $7F).
Again, it can leave the semaphore counter in overflowed state, but that's OK, it just temporarily blocks the other thread as well (very shortly and only with lots of bad luck).
I love the symmetry and the fact that it doesn't destroy any regs. You can't turn it into a multiple-anything though.