[TI ASM] atomics

Got questions? Got answers? Go here for both.

Moderator: MaxCoderz Staff

Post Reply
King Harold
Calc King
Posts: 1513
Joined: Sat 05 Aug, 2006 7:22 am

[TI ASM] atomics

Post by King Harold »

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: Select all

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.

Are more powerful atomic constructs possible on the z80?

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

Code: Select all

rlc (hl)
ret
ret
; changes itself into
sub a
ld b,$C9
ret
"useful" because the result is in both the Z and C flags. Bad because it kills b.

Code: Select all

rrc (hl)
ret
ret
; changes itself into
push hl
ld c,$C9
ret
Potentially useful because the push hl/ret automatically sends it into a loop when it fails. Bad because it kills c.

Code: Select all

sla (hl)
ret
; changes itself into
sub (hl)
ld h,(hl)
ret
Potentially useful because it's the shortest TaS-like instruction sequence I've found (if called with A = 10010110, the result is in the Z flag. call with A >= 10010110 for result in C flag) - kills A though, obviously (and sometimes h, but that's less of an issue)
Last edited by King Harold on Sun 01 May, 2011 4:25 am, edited 1 time in total.
Xeda
New Member
Posts: 42
Joined: Sun 06 Feb, 2011 7:08 am

Re: [TI ASM] atomics

Post by Xeda »

Sorry, I have no clue what any of this means :/ I just figured I would post so that you know somebody is reading this XD

I haven't tested this (I just threw it together), but this should give an idea for detecting if interrupts are enabled or not:

Code: Select all

;============================
;This will store the interrupt status in the P/V flag
;============================
     push bc         ;C5
     ld b,a          ;47
     ld a,i          ;ED57             ;This stores the interrupt status in the p/v flag. I think set= interrupts active
     ld a,b          ;78
     pop bc          ;C1
;============================
     push af         ;F5      ;This is to save the P/V flag
     di              ;F3
;More code
     pop af          ;F1
     ret po          ;E0               ;If this doesn't work, try pe XD
     ei              ;FB
     ret             ;C9
King Harold
Calc King
Posts: 1513
Joined: Sat 05 Aug, 2006 7:22 am

Re: [TI ASM] atomics

Post by King Harold »

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.
King Harold
Calc King
Posts: 1513
Joined: Sat 05 Aug, 2006 7:22 am

Re: [TI ASM] atomics

Post by King Harold »

Autocopy adds some more fun:

Code: Select all

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 that pattern can be still be useful. 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)
Post Reply