summaryrefslogtreecommitdiff
path: root/frontends/calculator/CEdev/lib/shared/atomic_load_decreasing_32.src
blob: 555e93e8b56f644a350ad7529c01b27874f54e4c (plain)
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