1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
; ---
; uint32_t atomic_load_decreasing_32(volatile uint32_t *p)
; ---
.def _atomic_load_decreasing_32
.assume adl=1
_atomic_load_decreasing_32:
pop hl
ex (sp),iy
push hl
; Temporarily disable interrupts.
ld a,i
di
; Read the value twice.
; first value read = cude, second value read = auhl
; Time between first and last byte read:
ld de,(iy) ; 2R
ld c,(iy+3) ; + 3F+1R
ld hl,(iy) ; + 3F+3R
ld a,(iy+3) ; + 3F+1R
; = 9F+7R
; = 56cc (assuming F = R = 3)
; ~ 1.17us (assuming 48MHz clock speed)
; + approximate worst-case LCD + USB DMA
; ~ 5us
; Re-enable interrupts if they were previously enabled.
jp po,no_ei
ei
no_ei:
; Compare and return the greater of the two values read.
;
; If the values differ, then the underlying value changed between reading the
; first byte of the first 32-bit read and the last byte of the second 32-bit
; read:
; * If a change occurs between the two 32-bit reads, then both values read
; remain valid.
; * If a change occurs in the middle of one of the 32-bit reads:
; * If the bytes already read change but the bytes yet to read do not
; change, then the value read is the valid value before the change.
; * If the bytes already read do not change but the yet still to read
; change, then the value read is the valid value after the change.
; * If the bytes already read and the bytes yet to read change, then the
; value read is an invalid mix of the values before and after the change.
;
; If the final case did not occur, then both values read are valid.
;
; If the final case occurred for only the second value read, then due to reading
; bytes in little-endian order, one to three upper bytes will reflect the
; decreased value after the change. Regardless of the lower bytes, the valid,
; first value read will be greater than the invalid, second value read.
;
; If the final case occurred for only the first value read, then due to reading
; bytes in little-endian order, one to three upper bytes will reflect the
; decreased value after the change, while the remaining one to three lower bytes
; will reflect the value before the change. If it is assumed that the value
; decreased by less than 256, then the upper bytes must have decreased through
; borrow and the lower bytes of the valid, second value read will be greater
; than the lower bytes of the invalid, first value read. And because the full
; second value read will reflect the the decreased value after the change, and
; therefore the two values read will only differ in the lower bytes, it follows
; that the full, valid, second valid read will be greater than the full,
; invalid, first value read. (Note that this assertion can be extended to
; include 256 because only one byte would change, making this whole case
; unreachable.)
;
; If the third case occurred for both values read, then both are invalid. There
; is no reasonable way to detect this, so it is assumed that this did not occur.
;
; Combining all of these cases and assumptions, the greater of the two values
; read will always be valid.
or a,a
sbc hl,de
sbc a,c ; auhl = second value read
; - first value read
jr c,swap
; second value read >= first value read
; ==> second value read is valid
add hl,de
adc a,c
ld e,a ; euhl = second value read
ret
swap:
; second value read < first value read
; ==> first value read is valid
ex de,hl
ld e,c ; euhl = first value read
ret
|