Skip to content

Commit

Permalink
lib/ce reviewed, useable items moved to lib/agon
Browse files Browse the repository at this point in the history
lib/ce reviewed, useable items moved to lib/agon - other disabled in linker_script
  • Loading branch information
pcawte committed Aug 10, 2023
1 parent 8a94fbb commit cedca73
Show file tree
Hide file tree
Showing 13 changed files with 899 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,18 @@ In the relevant: example, test or any other directory created at the same level.

- Added `vdp_solid_bitmap()` function

10/08/2023

- TI-84 CE specific library items (under `lib/ce`) disabled in `linker_script`. Other items shifted to `lib/agon`. Note some items might rely on HW timing so not give accurate results.

- atomic functions: should work, but not checked

- delay, sleep functions: rely on CPU clock cycle timing. Will run, but results will be off due to different clock speed

- random functions: okay

- zx0 & zx7 compression: okay

### To-Do / Known Issues:

- Testing / validation
Expand Down
61 changes: 61 additions & 0 deletions lib/agon/atomic_load_32.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
; ---
; uint32_t atomic_load_32(volatile uint32_t *p)
; ---
assume adl=1

section .text
public _atomic_load_32
_atomic_load_32:
pop hl
ex (sp),iy
push hl
ld c,0
retry:
; Wait for a different (increasing) amount of time before each retry to prevent
; getting caught in an endless loop of the value changing between reads with a
; fixed period equal to the amount of time it takes for each retry
inc c
ld b,c
wait:
djnz wait
; Read the value twice.
lea hl,iy+3
; Time between first and last byte read:
ld de,(iy) ; 2R
ld a,(hl) ; + 1F+1R
cp a,(hl) ; + 1F+1R
ld hl,(iy) ; + 3F+3R
; = 5F+7R
; = 48cc (assuming F = R = 3)
; = 1us (assuming 48MHz clock speed)
; Don't need to worry about DMA, just
; retry until not interrupted by DMA
; Retry if the values read differ.
;
; 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.
;
; Without reading the value again, there is not enough information to determine
; if the third case occurred. If it did occur, then one of the values is
; invalid. But there is also not enough information to determine which is
; invalid, and therefore which is valid. So reading the value must be retried.
;
; (Furthermore, if a change occurs in the middle of both 32-bit reads and the
; bytes already read change in both, then both values read become invalid.)
jr nz,retry
sbc hl,de
jr nz,retry
; Values don't differ, good to go.
add hl,de
ld e,a ; euhl = value read
ret
89 changes: 89 additions & 0 deletions lib/agon/atomic_load_decreasing_32.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
; ---
; uint32_t atomic_load_decreasing_32(volatile uint32_t *p)
; ---
assume adl=1

section .text
public _atomic_load_decreasing_32
_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 final 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
104 changes: 104 additions & 0 deletions lib/agon/atomic_load_increasing_32.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
; ---
; uint32_t atomic_load_increasing_32(volatile uint32_t *p)
; ---
assume adl=1

section .text
public _atomic_load_increasing_32
_atomic_load_increasing_32:
; CC: 202 + 9 * read_1_invalid + 3 * ie
; Timing assumes fetching from RAM (4cc) and reading from non-RAM (3cc).

; Read the argument.
pop hl
ex (sp),iy ; iy = p
push hl
; ^ 48 cc
; Temporarily disable interrupts.
ld a,i
di
; ^ 12 cc
; Read from the pointer twice: first into cude, second into auhl.
; Time between first and last byte read:
ld de,(iy) ; 2R
; ^ 21 cc
; 48 + 12 + 21 - 6 = 75 cc until first value captured if valid
ld c,(iy+3) ; + 3F + 1R
ld hl,(iy) ; + 3F + 3R
; ^ 36 cc
; 48 + 12 + 21 + 36 - 5 = 112 cc until second value captured if valid
ld a,(iy+3) ; + 3F + 1R
; == 9F + 7R
; == 57 cc
; + 9 * 19 cc = 171 cc (worst-case DMA)
; = 228 cc
; ^ 15 cc
; Re-enable interrupts if they were previously enabled.
jp po,no_ei
ei
no_ei:
; ^ 17 + 3 * ie cc
; Compare and return the lesser of the two values read.
;
; If the values are the same, then the underlying value must not have changed
; during the reads, so both values are valid.
;
; 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
; increased value after the change. Regardless of the lower bytes, the valid,
; first value read will be less 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
; increased 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
; increased by less than 256, then the upper bytes must have increased through
; carry and the lower bytes of the valid, second value read will be less than
; the lower bytes of the invalid, first value read. And because the full second
; value read will reflect the the increased 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 less 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 final 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 lesser 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
; ^ 16 cc
jr c,no_swap
; second value read >= first value read
; ==> first value read is valid
ex de,hl
ld e,c ; euhl = first value read
ret
; ^ 37 cc

no_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
; ^ 46 cc (including conditional jr into)
71 changes: 71 additions & 0 deletions lib/agon/delay.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
; void delay(uint16_t millis);

assume adl=1

if 0
; Minimal caller code (for timing calculation).
call _delay
; ^ 22 cc
end if

section .text
public _delay
_delay:
; Timing assumes fetching from RAM (4cc) and the first timer read being valid.

; Get millis.
pop de
ex (sp), hl ; hl = millis
push de
; ^ 48 cc
; Abort ASAP if millis == 0.
ld a, l
or a, h
ret z
; ^ 13 cc
; Get the start time ASAP.
push hl
call _clock ; euhl = start
; ^ 32 + cc(_clock) cc
; 22 + 48 + 13 + 32 + 48 + 75 = 238 cc until start time captured
ex (sp), hl ; hl = millis
push de
; Convert millis to clock ticks.
dec.s hl
xor a, a ; a = 0
ld e, a ; euhl = millis - 1
.multiplier_fp := (256 * 32768 + 1000 - 1) / 1000
assert .multiplier_fp shr 24 = 0
ld bc, .multiplier_fp ; aubc = ceil(256 * 32768 / 1e3)
assert ($FFFF * .multiplier_fp) shr 32 = 0
call __lmulu ; euhl = (millis - 1) *
; ceil(256 * 32768 / 1e3)
; https://docs.google.com/spreadsheets/d/1mZwDn6rEw3-uSQPuy48Ez0UWIvnggLiJxWft4u4OtcU/edit?usp=sharing
.correction_fp := 413
ld bc, .multiplier_fp + .correction_fp
add hl, bc
adc a, e ; auhl = millis *
; ceil(256 * 32768 / 1e3) +
; correction_fp
push af
inc sp
push hl
inc sp
pop hl ; uhl = (millis *
; ceil(256 * 32768 / 1e3) +
; correction_fp) /
; 256
; = ticks + correction
inc sp
xor a, a ; auhl = ticks + correction
; Join common code for the rest.
jp ___sleep_common.3


public _msleep
_msleep := _delay


extern ___sleep_common.3
extern __lmulu
extern _clock
Loading

0 comments on commit cedca73

Please sign in to comment.