| TIP: Click on subject to list as thread! | ANSI |
| echo: | |
|---|---|
| to: | |
| from: | |
| date: | |
| subject: | MSGID.ASM |
Hello All ...
=== Cut ===
;--------------------------------------------------------------------------
;
; MSGID.ASM, assembly language implementation of the procedures in the
; MSGID Pascal unit. Written and placed into the public domain, 1997-11-28,
; Rene Herman (2:282/1.11).
;
; Provides a function for generating (the number part of) MSGIDs (message
; identification strings) used to implement duplicate message detection in
; FidoNet Technology Networks (FTNs).
;
; According to FTS-0009, a MSGID (again, the number part of) must be an
; eight character hexadecimal number, uniquely identifying a message from a
; particular system for a period of at least three years. We comply (using
; 4 years for easy math) by constructing "base" MSGIDs according to:
;
; MSGID := ((Year mod 4) shl 30) or (Month shl 26) or (Day shl 21) or
; (Hour shl 16) or (Min shl 10) or (Sec shl 4)
;
; and using the right 4 bits as a continous counter. That is, we generate
; a base MSGID per the formula and continue adding 1 to it until we
; overflow the right 4 bits, at which time we start anew (at least delaying
; until the next second, in case we used up all our MSGIDs in one second).
; This method allows 16 MSGIDs to be generated per second, which should be
; enough. One extra free bit could be obtained by calculating the base
; MSGID as the number of seconds in this "Year mod 4" (or 3) period,
; providing for 32 possible MSGIDs per second, but this would require a lot
; of extra work, and probably isn't worth the trouble.
;
; Note that it is important to obtain the year, month, day, hour, minute
; and second in one indivisible operation. Most language libraries provide
; "GetTime" and "GetDate" functions, but using these
functions could cause
; trouble. Consider, for example, the situation that we start reading the
; date on 1997-28-11 / 23:59:59. Now, before we can read the time, the date
; changes, and when we get around to reading the time, we read 00:00:00.
; The program now believes it's 1997-28-11 / 00:00:00, a day ago, which
; could cause duplicate MSGIDs to be generated (if at that very moment a
; day ago we were also running). If we read the time first, we could wind
; up a day in the future, possibly causing the next-day run to generate
; duplicates. For this reason the code below gets the time and date values
; directly from CMOS.
;
; Of course, this approach has one major flaw, one that is shared by all
; methods that are based on the date and time, and that is the fact that
; there is no way to guard against someone setting back the clock. With
; the inaccuracy of PC clocks, and not to mention daylight savings time,
; this isn't a rare event.
;
; But unfortunately, short of introducing some concept like "network time",
; (or further elaborating on it?) with all Fido participants required to
; tranx their clocks to it on every connect with their uplink, I don't
; really see a solution for this problem anyway. Keeping special files
; around with the last date read or last MSGID generated certainly isn't
; the way to go. Files are bound to get corrupted or deleted, harddisks may
; crash, users may neglect to save the file when reinstalling software, and
; so on.
;
; Ignoring the problem seems to be the most intelligent solution to it, but
; if anyone has some comments on this, or any of the following, I'd be
; pleased to hear about them.
;
;--------------------------------------------------------------------------
.MODEL LARGE, PASCAL ; interfacing with a unit
PUBLIC GetMSGID ; publish functions
PUBLIC GetMSGIDStr
;--------------------------------------------------------------------------
;
; Equates for accessing the RTC CMOS registers
NMI_Disable = 080h;
RTC_Status = NMI_Disable or 10;
RTC_Year = NMI_Disable or 9;
RTC_Month = NMI_Disable or 8;
RTC_Day = NMI_Disable or 7;
RTC_Hour = NMI_Disable or 4;
RTC_Min = NMI_Disable or 2;
RTC_Sec = NMI_Disable or 0;
;--------------------------------------------------------------------------
;
; Private module data. The PrevMSGID and an array of hex characters for
; GetMSGIDStr.
.DATA
PrevMSGID DD -1
HexDigits DB '0123456789abcdef'
.CODE
;--------------------------------------------------------------------------
;
; GetRTC helper procedure for GetMSGID. Reads the Real Time Clock Values
; from CMOS (which are stored as packed-BCD). AL holds index (and NMI bit)
; and receives the (binary) result. Destroys CL.
GetRTC PROC NEAR
out 070h, al ; set index
out 0EBh, al ; i/o delay
in al, 071h ; get data
; Next 7 lines convert the retrieved packed-BCD byte into its binary form.
; Based on the observation that 10 * H + L = 16 * H + L - 6 * H. That is,
; subtract 6 times the high nibble from a packed-BCD byte to convert it to
; binary.
mov cl, al ; cl = 16H + L
shr cl, 1 ; cl = 8H + (L shr 1)
shr cl, 1 ; cl = 4H + (L shr 2)
and cl, 0FCh ; cl = 4H
sub al, cl ; org - 4H
shr cl, 1 ; cl = 2H
sub al, cl ; org - 4H - 2H = org - 6H
ret
GetRTC ENDP
;--------------------------------------------------------------------------
;
; GetMSGID function. Takes no parameters and returns a unique MSGID in
; DX:AX. Interface to it from Pascal as:
;
; function GetMSGID: LongInt; far;
GetMSGID PROC
; Get the previously saved (inited to -1) PrevMSGID and see if we can
; return it. That is, see if we haven't yet used up all sixteen MSGIDs
; we can construct with the clock value we retrieved earlier. If still
; some left, immediately exit with PrevMSGID + 1.
inc WORD PTR [PrevMSGID]
mov dx, WORD PTR [PrevMSGID + WORD]
mov ax, WORD PTR [PrevMSGID]
test al, 00Fh
jnz {at}{at}End ; exit if still some left.
; Our previous IDs all used up, need to get some new ones. See start of
; source for a description of our MSGID format, and the reason for getting
; the time and date values directly from CMOS. Now for some RTC pitfalls.
;
; Quoting The Undocumented PC - Frank van Gilluwe - ISBN 0-201-62277-7
;
; " Unfortunately reading the clock values is a bit more tricky than
; changing them. Why make our lives as programmers easy? The RTC chip
; has an internal update cycle once every second which takes about
; 2ms. During this internal update, reads from the time, date and
; alarm registers will get unpredictable data! If you just hope for
; the best, about 1 out of 500 tries will have bad data. Badd odds in
; my book, if you like stable, repeatable operation.
;
; O.K., so you really would like to get a valid value when you read the
; clock. To do so, disable interrupts, go into a tight loop, reading
; register A's 'update in progress' bit 7. When this transitions from
; high to low, you have all of 244ms to read the clock register you
; want. "
;
; Note the "when this transitions from high to low". That means we must
; wait for it to go high first, and then wait for it to go low again. If
; we we're to just check for it to be low, it might go high just after we
; checked, invalidating our data. Fortunately, we want to do this anyway,
; as it guarantees that we don't read the same second as the last time
; through. Using this approach, even another task in a multitasking
; environment won't be able to get the same second as we did. It is
; unfortunate that we need to keep interrupts disabled while waiting,
; since this means interrupts may be disabled for (almost) a full second,
; but it really can't be helped.
;
; NMI (Non Maskable Interrupt) is also controlled through the CMOS index
; register, and the book advices to disable it as well.
;
; There is the possibility that the clock is set to use AM/PM time keeping,
; in stead (sp?) of the more sane 24hour notation. If it is, we need to
; adjust to avoid generating duplicate MSGIDs at the same time AM and PM on
; the same day. We don't go through the trouble of converting to 24hour
; notation though, but just let the high bit of the 5bit hour value reflect
; PM. The fact that 12am is earlier in the day than 1am kills the monotonic
; increasing nature of the MSGID, but we really don't care, MSGIDs need be
; unique, not monotonic increasing. When AM/PM mode is in effect, the high
; bit (sign bit) of the value in CMOS will be set for PM, and clear for AM.
mov al, RTC_Status ; index status and disable NMI
cli ; disable interrupts
out 070h, al ; disable NMI and index reg. A
out 0EBh, al ; i/o delay
{at}{at}WaitRTCUpdate: ; wait for an update
in al, 071h
test al, 080h ; RTC update occuring?
jz {at}{at}WaitRTCUpdate ; > n
{at}{at}WaitNoRTCUpdate: ; wait till update past
in al, 071h
test al, 080h ; RTC update occuring?
jnz {at}{at}WaitNoRTCUpdate ; > y
; RTC just updated, now have approx 244ms (thats looong!) to get the time
; and date.
mov al, RTC_Day ; get day
call GetRTC
mov ah, al ; save
mov al, RTC_Year ; get year
call GetRTC
shr ax, 1 ; put year and days into place
rcr ax, 1
rcr ax, 1
mov dx, ax ; save away
mov al, RTC_Month ; get month
call GetRTC
shl al, 1
shl al, 1
or dh, al ; or the month into place
mov al, RTC_Hour ; get hours
call GetRTC
jns {at}{at}24HourFormatOrAM ; PM (if RTC in AM/PM mode) ?
xor al, 090h ; yes, clear sign, set bit 4
{at}{at}24HourFormatOrAM:
and dl, 0E0h ; zero out left over year bits
or dl, al ; or the hour into place
mov al, RTC_Min ; get minutes
call GetRTC
mov ah, al ; save
mov al, RTC_Sec ; get seconds
call GetRTC
out 070h, al ; reenable NMI (bit 7 clear)
sti ; reenable interrupts
shl al, 1 ; shift seconds into high al
shl al, 1
shl ax, 1 ; shift min/sec into high ax
shl ax, 1
; DX:AX now contains our MSGID. Note that the 4 shl's above have zeroed out
; the low 4 bits (our private counter). Store into PrevMSGID and return it.
mov WORD PTR [PrevMSGID + WORD], dx
mov WORD PTR [PrevMSGID], ax
{at}{at}End:
RET
GetMSGID ENDP
;--------------------------------------------------------------------------
;
; StoreHexWord helper procedure for GetMSGIDStr. Expects es:di to point to
; the last character of a 4 character string which it will fill with the
; ASCII-HEX representation of ax, and bx to point to a 16 char array of
; hex digits. Destroys al, cl. di decremented by 4.
StoreHexWord PROC NEAR
call {at}{at}StoreHexByte
mov al, ah
{at}{at}StoreHexByte:
mov cl, al
and al, 00Fh
xlat
mov es:[di], al
dec di
mov al, cl
shr al, 1
shr al, 1
shr al, 1
shr al, 1
xlat
mov es:[di], al
dec di
ret
StoreHexWord ENDP
;--------------------------------------------------------------------------
;
; GetMSGIDStr function. Simple wrapper around a GetMSGID call, using the
; above StoreHexWord helper to translate the retrieved MSGID to a hex
; string (a string[8] is required and sufficient). Takes no parameters and
; expects Pascal to provide it with the address of a return string. Use as:
;
; function GetMSGIDStr: string; far;
GetMSGIDStr PROC
call GetMSGID
mov bx, sp
les di, ss:[bx + CODEPTR]
add di, DWORD * 2
mov bx, OFFSET HexDigits
call StoreHexWord
mov ax, dx
call StoreHexWord
mov BYTE PTR es:[di], DWORD * 2
RET
GetMSGIDStr ENDP
END
;--------------------------------------------------------------------------
=== Cut ===
Rene
---
* Origin: rene.herman{at}tip.nl (2:282/1.11)SEEN-BY: 20/10 200/0 201/0 100 200 209 300 400 505 600 203/600 204/450 205/0 SEEN-BY: 206/0 270/101 490/21 633/267 270 @PATH: 282/1 280/801 270/101 201/505 633/267 |
|
| SOURCE: echomail via fidonet.ozzmosis.com | |
Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.