davex-mg-utils/tardis.s

659 lines
20 KiB
ArmAsm

; %help
; tardis - Get date/time from TimeLord server.
;
; options:
; -v Verbose mode, prints server and other info
; -n <nbp-name> Specify NBP query, default =:TimeLord@*
; -p Set ProDOS global page date/time
; -y Patch ProDOS year table to use returned year
; -s <type> Set a clock of <type>, current supported
; types: none
; -e <hours> Adjust time eastward
; -w <hours> Adjust time westward
; %hend
; Notes: AFP epoch is 12:00 AM January 1, 2000 GMT.
; Mac epoch is 12:00 AM January 1, 1904 GMT.
; the difference is 3029529600 ($b492f400) seconds
; Mac epoch is returned by TimeLord.
; AFP epoch is expected by WS Card ConvertTime function.
; The general algorithm is:
; Request the date from TimeLord.
; Subtract the epoch difference from the response to get AFP time.
; Use the FIConvertTime call to convert to ProDOS date and time.
; That gives us month/day/year/hour/minute
; To get seconds:
; Convert the ProDOS date and time back to AFP time.
; This number should be <= the AFP time we got above because the seconds are assumed
; to be 0.
; Subtract to get the seconds.
; In practice this does not work with the Apple II Workstation Card because its
; FIConvertTime function is buggy. It is sometimes off by 2 mins or more and the
; reverse conversion gives garbage.
; The IIgs this algo works perfect.
; So for now we don't give seconds on a IIe.
; Should probably rewrite this to just do the math rather than use FIConverTime.
.pc02
.include "davex-mg.inc"
;ch = $24 ; cursor horizontal pos
TL_OK = 12
TL_BAD = 10
entname = filebuff2 ; buffer to build NBP entity name
NBPBuf = filebuff3 ; buffer for NBP request
NBPBufSz = $0400 ; size of the file buffer
P8DtTm = $bf90
;prbyte = $fdda
sptr = xczpage
sptr2 = sptr+2
stemp = sptr2+2
verbose = stemp+1 ; verbose flag
seconds = verbose+1 ; holds seconds
notgs = seconds + 1
DX_start dx_mg_auto_origin ; load address
DX_info $03,$12,dx_cc_iie_or_iigs,$00
DX_ptab
DX_parm 'v',t_nil ; verbose
DX_parm 'n',t_string ; name
DX_parm 'z',t_string ; zone
DX_parm 'p',t_nil ; set prodos time
DX_parm 'y',t_nil ; patch year table
DX_parm 's',t_string ; set clock
DX_parm 'e',t_int1 ; east adjust
DX_parm 'w',t_int1 ; west adjust
DX_end_ptab
DX_desc "Get time from TimeLord server."
DX_main
sec
jsr $FE1F ; GS ID
ror notgs ; notGS now negative if it's not a GS
cli ; appletalk requires interrupts
ATcall inforeq
bcc :+
jmp noatalk
: stz verbose
lda #'v'|$80 ; verbose
jsr xgetparm_ch
bcs :+
inc verbose
: lda #<entname
sta sptr
lda #>entname
sta sptr+1
; get name
lda #'n'|$80 ; NBP name param
jsr xgetparm_ch
bcc :+ ; was specified
; name param not given, use default
lda #<entname
sta sptr
lda #>entname
sta sptr+1
ldy #$00
ldx #<defname
lda #>defname
jsr copystr
ldx #<deftype
lda #>deftype
jsr copystr
ldx #<defzone
lda #>defzone
jsr copystr
jmp doit
: ldx #$80 ; name, return wildcard if not given
jsr get_nbp
bcs badnbp
inx
txa
jsr addsptr
; get type
lda #'n'|$80
jsr xgetparm_ch
ldx #$01 ; type, return empty if not given
jsr get_nbp
bcc :+
ldy #$00
ldx #<deftype
lda #>deftype
jsr copystr
tya
bra :++ ; to addsptr
: inx
txa
: jsr addsptr
; get zone
lda #'n'+$80
jsr xgetparm_ch
ldx #$82 ; zone, return * if not given
jsr get_nbp
bcc doit
badnbp: lda #$01
jsr xredirect
jsr xmess
asc_hi "Bad NBP name."
.byte $00
bra exiterr1
doit: lda #<entname
sta sptr
lda #>entname
sta sptr+1
lda verbose
beq :+ ; don't print unless verbose
jsr prnbptup
jsr xmess
.byte $8d,$00
; Locate TimeLord on network
: ATcall lookup
bcc :+ ; no error, don't bail
notlord: lda #$01
jsr xredirect
jsr xmess
asc_hi "No TimeLord found!"
.byte $00
exiterr1: jmp exiterr
: lda matches ; check # matches
beq notlord ; no matches
; Copy found address/socket to request
ldx #3
: lda NBPBuf,x
sta ATPaddr,x
dex
bpl :-
lda verbose
beq :+
; display NBP entry if verbose
lda bufp+1 ; set up pointer to response buffer
sta sptr+1
lda bufp
sta sptr
jsr prnbpent
; now make ATP request to TimeLord
: lda #$01
sta ATPbmap
ATcall ATPparms
bcc :+
jmp notime ; if error, bail now
; see if verbose
: lda #'v'|$80
jsr xgetparm_ch
bcs :++ ; skip if not verbose
; display raw returned data in hex in little-endian format
ldx #$03
: lda To,x
phx
jsr prbyte
plx
dex
bpl :-
; display timelord status byte from user bytes
lda #'/'|$80
jsr cout
lda Status
jsr prbyte
lda #$8d
jsr cout
; check status
: lda Status
cmp #TL_OK
bne notime1
; now do big-endian subtraction of the base offset
; and simultaneously put the computed value in From
sec
ldx #$03
: lda To,x
sbc Base,x
sta From,x
dex
bpl :-
; Apply user-requested adjustments:
lda #'e'|$80
jsr xgetparm_ch
bcs ckwest
cpy #$01
bcc ckwest
; now do big-endian addition of eastward adjustment
: clc
ldx #$03
: lda From,x
adc Hour,x
sta From,x
dex
bpl :-
dey
bne :--
ckwest: lda #'w'|$80
jsr xgetparm_ch
bcs convert
cpy #$01
bcc convert
; do big-endian subtraction of westward adjustment
: sec
ldx #$03
: lda From,x
sbc Hour,x
sta From,x
dex
bpl :-
dey
bne :--
; Use the WS card to convert value in From to ProDOS format in To
convert: ATcall CvtParms
bcs notime1 ; bail if error
ATcall ResetSec ; sigh... weird firmware
bcs notime1
bit notgs
stz seconds
bmi setp8 ; skip calculate seconds if not IIgs
; calculate seconds
; Copy converted values to the other convert parm list
ldx #$03
: lda To,x
sta SecFrom,x
dex
bpl :-
ATcall SecParms ; convert back
bcc :+
notime1: jmp notime ; bail if error
: lda From+3
sec
sbc SecTo+3
sta seconds
; set Prodos date/time if asked
setp8: lda #'p'|$80 ; set prodos date/time?
jsr xgetparm_ch
bcs nosetp8 ; skip if -p not given
; Copy converted values to the global page
ldx #$03
: lda To,x
sta P8DtTm,x
dex
bpl :-
; patch year table?
nosetp8: lda #'y'|$80
jsr xgetparm_ch
bcs nosetyt
lda $c08b
lda $c08b
ldx #$03
: lda $d7b4,x ; tdays table, at september
cmp tdaysv,x ; that what ProDOS has?
bne setyterr ; not the thunderclock driver
dex
bpl :-
lda To+1 ; year (b7-1), upper bit of month (b0)
lsr
ldx #6
: sta $d7b8,x ; address of year table
dex
bpl :-
bit $c082 ; ROM back
bra nosetyt
setyterr: bit $c082 ; ROM back
lda #$01
jsr xredirect
jsr xmess
asc_hi "Can't patch clock driver!"
.byte $00
bra exiterr
nosetyt: ; TODO: set NSC or ThunderClock or something
; Display date/time
ldy To
lda To+1
jsr xpr_date_ay
jsr xmess
asc_hi " "
.byte $00
ldy To+2
lda To+3
jsr xpr_time_ay
bit notgs
bmi nosec
jsr xmess
asc_hi " +"
.byte $00
ldy seconds
cpy #10
bcs :+
lda #'0'|$80
jsr cout ; leading zero
: ldy seconds
lda #$00 ; zero high byte
jsr xprdec_2 ; print seconds
jsr xmess
.byte "s"
.byte $8d,$00
rts
nosec: lda #$8d
jsr cout
rts
notime: lda #$01
jsr xredirect
jsr xmess
asc_hi "Error getting date/time!"
.byte $00
bra exiterr
noatalk: lda #$01
jsr xredirect
jsr xmess
asc_hi "AppleTalk offline!"
.byte $00
exiterr: lda #$ff
jsr xredirect
jmp xerr
; ***
; get_nbp - get part of a name at ay and copy to sptr
; x = 0:name 1:type 2:zone, +$80 sub wildcard or default if none given
; return: (sptr) = string, x = length of it
.proc get_nbp
sty sptr2
sta sptr2+1
stx stemp
ldy #$00
sty colon ; init these
sty at
lda sptr ; copy sptr to self-modifying wrtdest
sta wrtdest+1
lda sptr+1
sta wrtdest+2
jsr incdest ; and move to first char position
lda (sptr2),y ; length
beq notfound ; zero, just give up
sta end
; find the delimiters
tay
: lda (sptr2),y
cmp #':'
bne notcolon
sty colon
notcolon: cmp #'@'
bne nxtdelim
sty at
nxtdelim: dey
bne :-
; now make sure that if @ is given, it is after :
lda at
beq :+
cmp colon
bcc bad
; now get the part requested
: lda stemp
and #$7f
beq getname
cmp #$01
beq gettype
getzone: ldy at
beq notfound
cpy end
beq notfound
; need to copy from at(+1) to end
docopy: ldx #$00
: iny
lda (sptr2),y
jsr wrtdest
cpy end ; was that the last char
bcc :- ; nope, next char
ldy #$00
txa
sta (sptr),y ; save copied length
clc
rts ; and return
getname: ldy colon
bne :+
ldy at
beq :++
: dey
sty end
: ldy end
beq notfound
ldy #$00 ; initially at pos 0
; need to copy from pos 1 to end
beq docopy ; always
gettype: ldy colon
beq notfound ; early out if no colon
cpy end
beq notfound
ldy at
beq :+ ; use end as-is
dey ; otherwise end at pos before @
sty end
: ldy colon
; need to copy from colon(+1) to end
bne docopy ; should be always
notfound: lda stemp
and #$80
bne :+ ; if client asked for a default
lda #$00
tay
tax ; x is officially length of string result
sta (sptr),y ; put a zero in destination
bad: sec ; tell client we gave an empty string
rts
: ldx #$01 ; length of default
ldy #$00
txa
sta (sptr),y
iny
lda stemp
cmp #$82 ; want default zone? ('*')
bne :++ ; nope
lda #'*'
: sta (sptr),y
clc
rts
: lda #'=' ; wildcard for name or type
bne :-- ; always
wrtdest: sta $ffff
inx ; inc count of copied chars
incdest: inc wrtdest+1
bne :+
inc wrtdest+2
: rts
colon: .byte $00
at: .byte $00
end: .byte $00
.endproc
;
; print an NBP entry at sptr
.proc prnbpent
ldy #$00 ; offset into entry, net number low byte
lda (sptr),y ; big end of network num
pha
iny
lda (sptr),y
tay
pla
jsr xprdec_2
jsr xmess
asc_hi "."
.byte $00
; print node
ldy #$02
lda (sptr),y
tay
lda #$00
jsr xprdec_2
jsr xmess
asc_hi ":"
.byte $00
; print socket
ldy #$03
lda (sptr),y
tay
lda #$00
jsr xprdec_2
jsr xmess
asc_hi " " ; print space in case output is not to screen.
.byte $00
;lda #20 ; Position NBP tuple on screen.
;sta ch
lda #$05 ; offset to NBP tuple
jsr addsptr
jsr prnbptup
jsr xmess ; CR
.byte $8d,$00
rts
.endproc
; print an NBP tuple at sptr
; leave sptr at byte just after tuple
; does not print zone unless verbose flag set
.proc prnbptup
; print name
jsr prpas
jsr xmess
asc_hi ":"
.byte $00
; print type
jsr prpas
lda verbose
beq skipzone ; if not verbose, don't display @zone
jsr xmess
asc_hi "@"
.byte $00
; print zone
jsr prpas
bra :+
skipzone: ldy #$00
lda (sptr),y ; get length of zone name
inc a ; account for length byte
jsr addsptr ; and skip the lot
: rts ; done
.endproc
; increment sptr by a
.proc addsptr
clc
adc sptr
sta sptr
bcc :+
inc sptr+1
: rts
.endproc
; print pascal string at sptr
; leave sptr pointed at one past end
; of string
.proc prpas
ldy #$00
lda (sptr),y ; get length
tax
next: lda #$01
jsr addsptr
dex
bpl :+
rts
: lda (sptr),y ; get char
ora #$80 ; make printable
jsr cout
bra next
.endproc
; copy a pascal string to sptr offset by y
; a,x = source
; return: y = new offset after copied str
.proc copystr
sta sptr2+1
stx sptr2
sty stemp ; save offset
ldy #$00
lda (sptr2),y ; get number of chars
tax ; to copy
ldy stemp ; get the offset
sta (sptr),y ; store the length byte
inc stemp ; increment the offset
inc sptr2 ; next source char
bne :+
inc sptr2+1
: ldy #0
; copy loop
: phy
lda (sptr2),y
ldy stemp
sta (sptr),y
inc stemp
ply
iny
dex
bne :-
ldy stemp
rts
.endproc
;
; table to validate the P8 tclock driver, 4 bytes at $d7b4
tdaysv: .byte 242,20,51,81
;
inforeq: .byte 0,2 ; sync GetInfo
.word $0000 ; result code
.dword $00000000 ; completion address
thisnet: .word $0000 ; this network #
abridge: .byte $00 ; local bridge
hwid: .byte $00 ; hardware ID, IIgs only
romver: .word $00 ; ROM version, IIgs only
nodenum: .byte $00 ; node number
; some pointers & values for building names
defname: .byte 1,"=" ; object
deftype: .byte 8,"TimeLord" ; type
defzone: .byte 1,"*" ; zone
; Base offset for epoch conversion, in big-endian order
;Base: .byte $B4,$93,$56,$70 ; PDT
Base: .byte $b4,$92,$f4,$00 ; GMT - set timezone or use adjust options
; Hours adjustment value
Hour: .byte $00,$00,$0e,$10 ; 3600 seconds
; parameter list for NBPLookup
lookup: .byte 0,16 ; sync NBPLookup
.word $0000 ; result
.dword $00000000 ; completion
.dword entname ; pointer to name to find
.byte 4,4 ; 4 times, every 1 sec
.word $0000 ; reserved
.word NBPBufSz ; buffer size
bufp: .dword NBPBuf ; buffer loc
.byte 1 ; matches wanted
matches: .byte $00 ; matches found
; ATP request parameters
ATPparms: .byte 0,18 ; sync SendATPReq
.word $0000 ; result
.dword $00000000 ; compl. addr
.byte $00 ; socket #
ATPaddr: .dword $00000000 ; destination address
.word $0000 ; TID
.word $0000 ; req buffer size
.dword $00000000 ; req buffer addr
.dword $00000000 ; user bytes, $00 = get time
.byte $01 ; one response buffer
.dword BDS ; pointer to response BDS
.byte $00 ; ATP flags
.byte 4,4 ; try 4 times every 1/4 second
ATPbmap: .byte $00 ; bitmap of blocks to recieve
.byte $00 ; number of responses
.res 6 ; 6 bytes reserved
; BDS for ATP request
BDS: .word $0004 ; 4-byte buffer for basic response from TimeLord
.dword To ; Buffer pointer
Status: .dword $00000000 ; returned user bytes, first byte = 12 if OK
.word $0000 ; actual length
; Convert time parameters - P8 to network for calculating seconds
SecParms: .byte 0,$34 ; sync ConvertTime
.word $0000 ; result
.byte $01 ; 1 = ProDOS to AFP
SecFrom: .dword $00000000
SecTo: .dword $00000000
; Convert time to reset seconds to 0 because the firmware saves the seconds
; and reuses it when converting from P8 to network time.
ResetSec: .byte 0,$34 ; sync ConvertTime
.word $0000 ; result
.byte $00 ; 0 = from AFP to ProDOS, 1 = reverse
.dword $00000000 ; BIG ENDIAN, Jan 1, 2000 12:00 AM GMT
.dword $00000000 ; P8 Time
; Convert time parameters - network to P8
; note that ATP response is written to From
CvtParms: .byte 0,$34 ; sync ConvertTime
.word $0000 ; result
CvtDir: .byte $00 ; 0 = from AFP to ProDOS, 1 = reverse
From: .dword $00000000
To: .dword $00000000 ; initially contains time from ATP response
DX_end