In my high school, the bells were controller by a computer. I wrote a program the uses the RTC (Real Time Clock) to count down to the end of the period. For some reason, this always amazed people. Apparently nobody else ever noticed the bells were automated. Anyway, for some reason the program would crash randomly during the start-up sequence. When I put pauses in there to isolate the problem, it seemed to stop crashing (it actually just became more rare), and I never found the problem.
Here are the routines for the program. You'll need to look up some of the 84-only b_calls on WikiTI. By the way, this program won't work on an Nspire because it uses the index register half-registers.
Code: ;====== Routines ===============================================================
;------ Miscellaneous ----------------------------------------------------------
;cphlde:
; or a
; sbc hl,de
; add hl,de
; ret
;#include "utils.z80"
;------ Time Routines ----------------------------------------------------------
getCurrentTime:
; Returns current time in HLIX
call getRealCurrentTime
loadTimeBCDE(startTime)
call subBCDEfromHLIX
ret
getRealCurrentTime:
; Returns real current time in HLIX
ld hl, currentTime
in a, ($45)
ld ixl, a
in a, ($46)
ld ixh, a
in a, ($47)
ld l, a
in a, ($48)
ld h, a
in a, ($45)
cp ixl ; In the unlikely event that the time changes whilst we are reading it
jr nz, getCurrentTime
ret
setTime:
; Sets the time to HLIX.
ld a, ixl
out ($41), a
ld a, ixh
out ($42), a
ld a, l
out ($43), a
ld a, h
out ($44), a
ld a, 1
out ($40), a
ld a, 3
out ($40), a
ret
secToHMS:
; Converts seconds into hours, minutes, and seconds.
; Input: HLIX
; Output: CDE: HMS
ld a, l
push ix
pop bc
ld de, 60*60
call divABCbyDE
push bc ; bc has hours, hl minutes
push hl
pop bc
ld de, 60 ; a is already 0
call divABCbyDE ; bc now has mintes, hl seconds
ld e, l
ld d, c
pop hl
ld c, l
ret
HMStoSec:
; Converts an hours, minutes, and seconds time into seconds.
; Input: CDE
; Output: HLIX: seconds
di
push iy
push de
pop iy
ld hl, 60*60
ld e, c
ld d, 0
ld bc, 0 ; is already hours
call multBHLbyCDE
ld c, b
ld b, 0
push bc
push hl
ld hl, 60
push iy
pop de
ld e, d
ld d, 0
ld bc, 0
call multBHLbyCDE
ld c, b
ld b, 0
push hl
pop de
pop ix
pop hl
addBCDEtoHLIX
ld bc, 0
ld d, 0
ld e, iyl
addBCDEtoHLIX
pop iy
ei
ret
aToString:
; Makes a 2-digit long string out of A. Well, it could be three, but it's
; always at least two.
ld l, a
ld h, 0
ld de, OP1+1
call itoa
ld hl, OP1+1
ld a, (OP1+2)
or a
ret nz
dec hl
ld (hl), "0"
ret
dispTimeString:
; Makes a string out of a the current time, into OP2
; Input:
; - HLIX: time
; Output:
; - OP2: String
; - String is displayed
di
push iy
ld iy, OP2
call secToHMS
push de
ld a, c
call aToString
push hl
pop ix
ld a, (ix)
ld (iy), a
ld a, (ix+1)
ld (iy+1), a
ld a, ":"
ld (iy+2), a
pop de
push de
ld a, d
call aToString
push hl
pop ix
ld a, (ix)
ld (iy+3), a
ld a, (ix+1)
ld (iy+4), a
ld a, ":"
ld (iy+5), a
pop de
ld a, e
call aToString
push hl
pop ix
ld a, (ix)
ld (iy+6), a
ld a, (ix+1)
ld (iy+7), a
ld (iy+8), 0
pop iy
ei
ld hl, op2
b_call(_PutS)
ret
; ITOA
; --------------------------------------------------------------
; Converts a signed integer value to a zero-terminated ASCII
; string representative of that value (using radix 10).
; --------------------------------------------------------------
; INPUTS:
; HL Value to convert (two's complement integer).
; DE Base address of string destination. (pointer).
; --------------------------------------------------------------
; OUTPUTS:
; None
; --------------------------------------------------------------
; REGISTERS/MEMORY DESTROYED
; AF HL
; --------------------------------------------------------------
; Modifed to not be signed by nobody special
itoa:
push de
push bc
; Convert HL to digit characters
_DoConvert:
ld b, 0 ; B will count character length of number
m:
ld a, 10
b_call(_DivHLByA) ; HL = HL / A, A = remainder
push af
inc b
ld a, h
or l
jr nz, m
; Retrieve digits from stack
n:
pop af
or $30
ld (de), a
inc de
djnz n
; Terminate string with NULL
xor a
ld (de), a
pop bc
pop de
ret
;------ Math Routines ----------------------------------------------------------
;addBCDEtoHLIX:
; 32-bit addition routine
; Input:
; - HLIX: first operand
; - BCDE: second operand
; Output:
; - HLIX: sum
; - carry if overflow
; AHEM. Now a macro.
subBCDEfromHLIX:
; 32 subtraction routine
; is slightly more complicated.
; Input:
; - HLIX: number to subtract from
; - BCDE: number to subtract
; Output:
; - HLIX: result
; - carrynderflow
or a
push hl
push ix
pop hl
sbc hl, de
push hl
pop ix
pop hl
sub32nc:
sbc hl, bc
ret
;-------------------------------------------------------------------------------
; These routines are by Milos "baze" Bazelides, from the web page
; [url="http://baze.au.com/misc/z80bits.html"]http://baze.au.com/misc/z80bits.html[/url]
divABCbyDE:
; 24-bit over 16-bit divide
; Input:
; - ABC: Dividend
; - DE: Divisor
; Output:
; - ABC: Quotient
; - HL: Remainder
ld hl, 0
ld ixl, 24
divABCbyDEloop:
sll c ; unroll 24 times
rl b ; ...
rla ; ...
adc hl,hl ; ...
sbc hl,de ; ...
jr nc,$+4 ; ...
add hl,de ; ...
dec c ; ...
dec ixl
jr nz, divABCbyDEloop
ret
divHLbyC:
; 16-bit over 8-bit divide
; Input:
; - HL: Dividend
; - C: Divisor
; Output:
; - HL: Quotient
; - A: Remainder
xor a
ld b, 16
divHLbyCloop:
add hl,hl ; unroll 16 times
rla ; ...
cp c ; ...
jr c,$+4 ; ...
sub c ; ...
inc l ; ...
djnz divHLbyCloop
ret
;-------------------------------------------------------------------------------
; This routine is by sigma, retrived from
; [url="http://www.detachedsolutions.com/forum/mv/msg/1154/0/15/"]http://www.detachedsolutions.com/forum/mv/msg/1154/0/15/[/url]
multBHLbyCDE:
; 24-bit multiply routine
; Input:
; - BHL: factor
; - CDE: factor
; Output:
; - BHL: product
; - CY: 0
; If there was overflow:
; - BHL = garbage
; - CY = 1
; Disables interrupts
call MulS_24
ei
ret
MulS_24:
di
ld a,b
xor c
push af
xor c
; call m,Negate_BHL
ld a,c
or a
; call m,Negate_CDE
push iy
.db $FD
ld h,24
push hl
pop ix
xor a
sbc hl,hl
call MulLoop
pop iy
jp pe,Overflow
ld b,a
pop af
ret p
jp quit
Overflow:
pop af
scf
ret
MulLoop:
add hl,hl
adc a,a
ret pe
add ix,ix
rl b
jr nc,DontAdd
add hl,de
adc a,c
ret pe
DontAdd:
.db $FD
dec h
jr nz,MulLoop
ret
If you want, here's the rest of the program. I'd advise against using this code because it has that unfound bug.
Code: ; ==============================================================================
; Countdown timer, by Dr. D'nar
; October 9, 2008
; Bugs:
; - Random crashing on start-up.
; To-do:
; - getStartTime could be done with HMStoSec
;
; Note to self: I no longer fully understand this program. And I wrote it.
#include "ti83plus.txt"
;====== Equates ================================================================
; Constants
;nameLocation .equ $0000
bannerTextLoc .equ $0000
timeLocation .equ $0801
timeTextLoc .equ $0001
endLocation .equ $0802
pdEndLocation .equ $0002
remainTextLoc .equ $0003
remainLocation .equ $0803
helpTextLoc .equ $0005
maxNumOfPeriods .equ 50
fooFlags .equ xapFlag ; Mirage uses some Asm_Flags
timeIsUp .equ 0
statusFlags .equ xapFlag
runRestoreFlag .equ 1
; RAM
spaddr .equ appBackUpScreen
; 32-bit times are stored as follows:
; sample number:
; 0x11223344
; byte order in memory:
; 22114433
currentTime .equ spaddr+2
startTime .equ currentTime+4
targetTime .equ startTime+4
foo .equ targetTime+4
bar .equ foo+2
baz .equ bar+2
listPdsLeft .equ foo
listLoc .equ bar
currentPeriod .equ baz+2
numberOfPeriods .equ currentPeriod+1
currentPdData .equ numberOfPeriods+1
periodTimeTable .equ currentPdData+2
; Macros
#define saveTimeBCDE(xxxx) ld (xxxx), bc \ ld (xxxx+2), de
#define loadTimeBCDE(xxxx) ld bc, (xxxx) \ ld de, (xxxx+2)
#define saveTimeHLIX(xxxx) ld (xxxx), hl \ ld (xxxx+2), ix
#define loadTimeHLIX(xxxx) ld hl, (xxxx) \ ld ix, (xxxx+2)
#define breakPoint di \ xor a \ jr z, $ \ ei
#define addBCDEtoHLIX add ix, de \ adc hl, bc
#define debug
.list
;====== Header =================================================================
.org $9D93
.db t2ByteTok, tAsmCmp
xor a
jr nc, start
.db "Timer (10/28/08)",0
start:
bit indicRun, (iy+indicFlags)
jr z, noRunIndic
set runRestoreFlag, (iy+statusFlags)
jr runIndicCont
noRunIndic:
res runRestoreFlag, (iy+statusFlags)
runIndicCont:
res indicRun, (iy+indicFlags)
; Empty vars area (some may need to be zero)
ld hl, appbackupscreen
ld b, 700
initApp:
ld (hl), 0
inc hl
djnz initApp
ld (spaddr), sp
; Find model
in a, (2)
bit 5, a
; jp z, errModel
#ifdef debug
ld hl, 0
ld (currow), hl
ld a, '0'
b_call(_PutC)
b_call(_getkey)
#endif
; Load the lists.
ld hl, listPDHname
ld bc, periodTimeTable
call loadListData
jp c, errNoLists
ld hl, listPDMname
ld bc, periodTimeTable+1
call loadListData
jp c, errNoLists
ld hl, listPDSname
ld bc, periodTimeTable+2
call loadListData
jr doneListLoading
loadListData:
push bc
rst 20h ; Mov9ToOP1
rst 10h ; FindSym | hl vat de ram b rom
pop ix
ret c
push ix
ld a, b
or a
jp nz, errArchived
PLDcont:
inc de
ld a, (de)
or a
jp nz, errDimMismatch
dec de
ld a, (de)
cp maxNumOfPeriods
jp nc, errDimMismatch
ld a, (numberOfPeriods)
or a
jr nz, numberOfPeriodsIsInit
ld a, (de)
ld (numberOfPeriods), a
numberOfPeriodsIsInit:
ld a, (numberOfPeriods)
ld c, a
ld a, (de)
cp c
jp nz, errDimMismatch
; ---
ld hl, 1
ld ixl, a
pop bc
loadLoop:
push de
push hl
push bc
di
ex af, af' ; I'm too lazy to figure out which need to be saved
exx
ld hl, errListLoad
call APP_PUSH_ERRORH
exx
ex af, af'
; No real need to ei yet.
b_call(_GetLToOP1) ; hl ele num; de pointer; OP1 output; hl ptr to next ele
b_call(_ConvOP1)
di
ex af, af'
exx
call APP_POP_ERRORH
exx
ex af, af'
ei
pop bc
ld (bc), a
inc bc
inc bc
inc bc
inc bc
pop hl
pop de
inc hl
dec ixl
jr nz, loadLoop
or a
ret
doneListLoading:
#ifdef debug
ld hl, 0
ld (currow), hl
ld a, '1'
b_call(_PutC)
b_call(_getkey)
#endif
; Convert the useless HMS times we just loaded
ld a, (numberOfPeriods)
ld (listPdsLeft), a
ld hl, periodTimeTable
ld (listLoc), hl
listProcessLoop:
ld hl, (listLoc)
ld c, (hl)
inc hl
ld d, (hl)
inc hl
ld e, (hl)
call HMStoSec
push hl
pop bc
push ix
pop de
ld hl, (listLoc)
ld (hl), c
inc hl
ld (hl), b
inc hl
ld (hl), e
inc hl
ld (hl), d
inc hl
ld (listLoc), hl
ld a, (listPdsLeft)
dec a
ld (listPdsLeft), a
jr nz, listProcessLoop
ld a, (numberOfPeriods)
inc a
inc a
ld (numberOfPeriods), a
ld (hl), $01
inc hl
ld (hl), $00
inc hl
ld (hl), $80
inc hl
ld (hl), $51
inc hl
#ifdef debug
ld hl, 0
ld (currow), hl
ld a, '2'
b_call(_PutC)
b_call(_getkey)
#endif
getStartTime:
; Figure out when today began
call getRealCurrentTime
saveTimeHLIX(startTime)
push ix
b_call(_GetTime)
call getRealCurrentTime
push ix
pop de
pop hl
or a
sbc hl, de
jr nz, getStartTime
; Calculate the number of seconds since midnight
b_call(_PopRealO1)
b_call(_ConvOP1)
push de
b_call(_PopRealO1)
b_call(_ConvOP1)
ld bc, 0
ld hl, 60
call multBHLbyCDE
pop bc
add hl, bc
push hl
b_call(_PopRealO1)
b_call(_ConvOP1)
ld bc, 0
ld hl, 60*60
call multBHLbyCDE
ld c, b
ld b, 0
push hl
pop de
; BCDE now has the (hours since midnight) turned into (seconds since midnight)
ld hl, 0
pop ix
addBCDEtoHLIX
; HLIX now has the number of seconds since midnight
push hl
pop bc
push ix
pop de
loadTimeHLIX(startTime)
call subBCDEfromHLIX
saveTimeHLIX(startTime)
#ifdef debug
ld hl, 0
ld (currow), hl
ld a, '3'
b_call(_PutC)
b_call(_getkey)
#endif
findCurrentPeriod:
; Now we want to know what period it is.
ld a, (numberOfPeriods)
ld (listPdsLeft), a
ld hl, periodTimeTable
ld (listLoc), hl
findPeriodLoop:
ld a, (listPdsLeft)
dec a
ld (listPdsLeft), a
jp z, errCannotFindPeriod
call getCurrentTime
push hl
ld hl, (listLoc)
ld c, (hl)
inc hl
ld b, (hl)
inc hl
ld e, (hl)
inc hl
ld d, (hl)
inc hl
ld (listLoc), hl
pop hl
call subBCDEfromHLIX
jr nc, findPeriodLoop
ld hl, (listLoc)
dec hl
dec hl
dec hl
dec hl
ld (currentPdData), hl
loadTimeHLIX(startTime)
addBCDEtoHLIX
saveTimeHLIX(targetTime)
#ifdef debug
ld hl, 0
ld (currow), hl
ld a, '4'
b_call(_PutC)
b_call(_getkey)
#endif
;doneListProcessing:
; Okay, we're past the places likely to get errors, so clear the screen and all.
b_call(_ClrScrn)
; Time for the main loop
ld hl, bannerTextLoc
ld (CurRow), hl
ld hl, bannerText
set TextInverse, (iy+TextFlags)
b_call(_PutS)
res TextInverse, (iy+TextFlags)
ld hl, helpTextLoc
ld (CurRow), hl
ld hl, helpText
b_call(_PutS)
ld hl, timeTextLoc
ld (CurRow), hl
ld hl, timeText
b_call(_PutS)
ld hl, pdEndLocation
ld (CurRow), hl
ld hl, pdEndText
b_call(_PutS)
ld hl, remainTextLoc
ld (CurRow), hl
ld hl, remainText
b_call(_PutS)
ld hl, endLocation
ld (CurRow), hl
loadTimeHLIX(targetTime)
loadTimeBCDE(startTime)
call subBCDEfromHLIX
call dispTimeString
res timeIsUp, (iy+fooFlags)
timerLoop:
; Display current time
ld hl, timeLocation
ld (CurRow), hl
call getCurrentTime
call dispTimeString
; Display time remaining
ld hl, remainLocation
ld (CurRow), hl
call getRealCurrentTime
saveTimeHLIX(currentTime)
push hl
pop bc
push ix
pop de
loadTimeHLIX(targetTime)
call subBCDEfromHLIX
call dispTimeString
; Wait until next second.
in a, ($45)
ld b, a
loopWaitLoop:
; halt?
b_call(_GetCSC)
cp skClear
jp z, quitAndClear
cp skStore
jr z, sync
cp skAdd
jr z, syncUp
cp skSub
jr z, syncDown
in a, ($45)
cp b
jr z, loopWaitLoop
bit timeIsUp, (iy+fooFlags)
jp nz, getStartTime
loadTimeBCDE(currentTime)
loadTimeHLIX(targetTime)
call subBCDEfromHLIX
ld a, ixh
or a
jr nz, timerLoop
ld a, ixl
or a
jr nz, timerLoop
set timeIsUp, (iy+fooFlags)
jp getStartTime
sync:
call getRealCurrentTime
call setTime
jp getStartTime
syncUp:
loadTimeHLIX(targetTime)
call setTime
jp getStartTime
syncDown:
ld hl, (currentPdData)
ld de, periodTimeTable
or a
sbc hl, de
add hl, de
jr z, loopWaitLoop
dec hl
ld d, (hl)
dec hl
ld e, (hl)
dec hl
push de
pop ix
ld d, (hl)
dec hl
ld e, (hl)
push de
pop hl
loadTimeBCDE(startTime)
addBCDEtoHLIX
call setTime
jp getStartTime
;-------------------------------------------------------------------------------
errListLoad:
push af
b_call(_NewLine)
ld hl, errListLoadText
b_call(_PutS)
pop af
ld l, a
ld h, 0
b_call(_DispHL)
ld a, $29
b_call(_PutC)
jr quit
quitAndClear:
b_call(_ClrScrn)
b_call(_HomeUp)
jr quitNoNewLine
errArchived:
ld hl, errArchivedText
jr quitErrMsg
errDimMismatch:
ld hl, errDimMismatchText
jr quitErrMsg
errCannotFindPeriod:
ld hl, errCannotFindPeriodText
jr quitErrMsg
errNoLists:
ld hl, errNoListsText
jr quitErrMsg
errModel:
ld hl, errModelText
quitErrMsg:
push hl
b_call(_NewLine)
pop hl
b_call(_PutS)
quit:
b_call(_NewLine)
quitNoNewLine:
bit runRestoreFlag, (iy+statusFlags)
jr z, quitNoRunIndic
set indicRun, (iy+indicFlags)
jr quitRunIndicCont
quitNoRunIndic:
res indicRun, (iy+indicFlags)
quitRunIndicCont:
; b_call(_RunIndicOn)
res DonePrgm, (iy+DoneFlags)
res OnInterrupt, (iy+OnFlags)
ld sp, (spaddr)
ret
; ROUTINES GO HERE!
;====== Data ===================================================================
; 1234567890123456
errModelText:
.db "A TI-84/SE is required.", 0
errNoListsText:
; lPDH and lPDM required.
.db $DC, "PDH and ", $DC, "PDM required.", 0
errCannotFindPeriodText:
.db "Could not find period.", 0
errArchivedText:
.db "Lists are archived.", 0
errDimMismatchText:
.db "Lists do not match.", 0
errListLoadText:
.db "List load error (#", 0
listPDHname:
.db RealObj, tVarLst, "PDH", 0
listPDMname:
.db RealObj, tVarLst, "PDM", 0
listPDSname:
.db RealObj, tVarLst, "PDS", 0
; 1234567890123456
timeText:
.db "Time:", 0
pdEndText:
.db "Pd End:", 0
remainText:
.db "Remain:", 0
bannerText:
.db " Period Timer ", 0
helpText:
; 1234567890123456
.db "STO for sync "
.db "+ sync to next "
.db "- sync to prev", 0
.end
.end
If anyone finds the problem, do tell me. I'd like to actually publish this.
Last edited by Guest on 11 Sep 2009 06:31:04 am; edited 1 time in total |