/* MSF clock decoder board firmware CS&E4 project 1984/5 /* /* Assemble with EUCSD Z80 assembler and blow into one 2716 /* EPROM The eprom resides from 16_XXXX0000 onwards, where /* XXXX are the switch positions of the clock decoder board. /* /* Copyright (C) David A Kerr & EUCSD 1985 All Rights Reserved. /* University of Edinburgh /* Computer Science Department. /* /* Control Processor Communication: /* 16_0800 %record %format TIME {14 bytes} /* (%byte control1, control2, turn, null /* hundreds, seconds, minutes, hours, /* day of week, day of month, month, year, century, BST) /* 16_080E %record %format COMPACT TIME {26 bytes} /* (%byte control1, control2, turn, null, %string(21) time) /* 16_0828 %record %format FULL TIME {60 bytes} /* (%byte control1, control2, turn, null, %string(55) time) /* 16_0864 %record %format LAST UPDATE {10 bytes} /* (%byte control1, control2, turn, null, /* year, month, day of month, day of week, hour, minute) /* /* /* The Control Processor board of the APM system can read information /* (the time) from the record formats at 'RecA', 'RecB', 'RecC', the /* format of the record is such that the first three bytes are used in /* the control of "peterson's solution" to the semaphore problem. In /* addition 'RecD' contains the time when the internal clock registers /* were last updated from the MSF time signal received (error free!) /* stack=16_0FFE loc 16_0800 RecA: BLOCK 14 /* TIME RecB: BLOCK 26 /* COMPACT TIME RecC: BLOCK 60 /* FULL TIME RecD: BLOCK 10 /* LAST UPDATE str1: BLOCK 22 /* COMPACT TIME temporary scratch area str2: BLOCK 56 /* FULL TIME temporary scratch area null1: 0 twoh: 0 hund: 0 /* TIME clock's internal registers secs: 0 mins: 0 hour: 0 dwek: 0 dmth: 0 mnth: 0 year: 0 cent: 0 bstgmt: 0 tyear: 0 /* temporary store for received data tmnth: 0 tdmth: 0 tdwek: 0 thour: 0 tmins: 0 parity: 0 /* parity information thold1: 0 /* decision threshold for first level data thold2: 0 /* decision threshold for second level data Rxed: 0 /* valid time received yet ? number: 0 /* number of 'samples' in this 'state' last8: 0 /* last 8 'bits' sofar: W 0 /* total samples read this minute state: W 0 /* noise suppression state machine state frame: 0 /* used to search for framing code in: 0 /* 'in' pointer to array latest: BLOCK 256 /* data transitions array leds: 0 /* DISPLAY 0 0 0 0 0 loc 16_3000 dig0: 0 /* LED display driver (74C917) registers dig1: 0 dig2: 0 dig3: 0 dig4: 0 dig5: 0 loc 16_3800 input: 0 /* address of input port /************************************************************** loc 16_0000 /* reset position, setup stack pointer LD SP,stack IM1 /* and interrupt mode, then continue JP reset loc 16_0008 /* restart not used, so just return RET loc 16_0010 /* restart not used, so just return RET loc 16_0018 /* restart not used, so just return RET loc 16_0020 /* restart not used, so just return RET loc 16_0028 /* restart not used, so just return RET loc 16_0030 /* restart not used, so just return RET loc 16_0038 /* comes here on interrupt CALL int /* call the routine to service interrupt EI /* re-enable interrupts RETI /* and return loc 16_0066 /* NMI routine, as yet does nothing HALT /* but could be indication of power failure NOP /* end of the interrupt routines, now the copyright notice /* then the start of the code propper /* Copyright (C) David A Kerr & EUCSD 1985 All Rights Reserved. Copy: 'C';'o';'p';'y' 'r';'i';'g';'h' 't';' ';'(';'C' ')';' ';'D';'a' 'v';'i';'d';' ' 'A';' ';'K';'e' 'r';'r';' ';'&' ' ';'E';'U';'C' 'S';'D';' ';'1' '9';'8';'5';' ' 'A';'l';'l';' ' 'R';'i';'g';'h' 't';'s';' ';'R' 'e';'s';'e';'r' 'v';'e';'d';'.' /************************************************************** /************************************************************** /* this is where it all happens, or rather all starts reset: LD A,0 LD B,0 LD HL,16_0800 init: LD (HL),A INC HL DJNZ init /* resets first 256 bytes of RAM to zero /* setup state for the MSF acqusition section LD HL,S0 LD (state),HL /* no valid MSF time received yet LD A,0 LD (Rxed),A /* setup a default date... my 21st birthday! LD A,2 LD (dwek),A LD A,1 LD (dmth),A LD (mnth),A LD A,85 LD (year),A LD A,19 LD (cent),A /* decision thresholds LD A,30 LD (thold1),A /* level 1 data bit decision threshold LD A,50 LD (thold2),A /* level 2 data bit decision threshold /* setup the control fields of the records LD A,-1 /* set all to non zero LD IX,RecA /* first record LD (IX+0),A /* control1 LD (IX+1),A /* control2 LD (IX+2),A /* turn LD IX,RecB /* ditto for second LD (IX+0),A LD (IX+1),A LD (IX+2),A LD IX,RecC /* and third LD (IX+0),A LD (IX+1),A LD (IX+2),A LD IX,RecD /* and fourth LD (IX+0),A LD (IX+1),A LD (IX+2),A /* setup lengths of the two strings LD A,21 LD (str1),A /* first string always 21 characters long LD A,0 LD (str2),A /* second one variable, start at zero EI loop: NOP JR loop /************************************************************** /************************************************************** /* comes here on interrupt int: CALL acquir /* call acquire data LD IX,twoh /* address of two hundreds LD A,2 INC (IX+0) /* two = two + 1 CP (IX+0) /* two = 2 ? CALL Z,inctim /* call increment time if equal CALL display /* display hours/mins/secs on LEDs LD HL,hund /* address of time start LD BC,10 /* length of time info LD DE,RecA+4 CALL trans /* copy it to common area for APM CALL compac /* copy time in compact format CALL fullt /* copy time in full format RET /* return from interrupt /************************************************************** /************************************************************** /* THE CLOCK BIT, following constitues a free running clock. /* /* area pointed to by IX is copied to the LED display dis: LD A,(IX+0) LD (dig0),A LD A,(IX+1) LD (dig1),A LD A,(IX+2) LD (dig2),A LD A,(IX+3) LD (dig3),A LD A,(IX+4) LD (dig4),A LD A,(IX+5) LD (dig5),A RET /************************************************************** /* transfer from HL length BC into the buffer number in DE /* deploys "Peterson's solution" for lock out. Registers /* returned unnaffected trans: PUSH DE POP IX LD (IX-4),0 /* control1=0 LD (IX-2),0 /* turn=0 LD A,0 uloop: CP (IX-3) /* test control2=0 ?? JR NZ,gotit /* exit loop if not zero CP (IX-2) /* test turn=0 ?? JR Z,uloop /* loop if zero gotit: LDIR /* Z80 block transfer (from HL,to DE,len BC) LD (IX-4),-1 /* control1#0 to release semaphore RET /************************************************************** /* Increment the time by one hundredth of a second inctim: LD (IX+0),0 /* one hundredth of a second has passed LD A,100 INC (IX+1) /* increment hundredths CP (IX+1) /* = 100 ?? RET NZ /* no so return LD (IX+1),0 /* yes so clear and continue /* one second has passed LD A,60 INC (IX+2) /* increment seconds CP (IX+2) /* = 60 ?? RET NZ /* no so return LD (IX+2),0 /* yes so clear and continue /* one minute has passed INC (IX+3) /* increment minutes CP (IX+3) /* = 60 ?? RET NZ /* no so return LD (IX+3),0 /* yes so clear and continue /* one hour has passed LD A,24 INC (IX+4) /* increment hours CP (IX+4) /* = 24 ?? RET NZ /* no so return LD (IX+4),0 /* yes so clear and contunue /* one day has passed LD A,7 INC (IX+5) /* increment day of week CP (IX+5) /* = 7 ?? JR NZ,incday /* no so return LD (IX+5),0 /* yes so clear and contunue incday: LD A,(mnth) LD DE,year CALL nodays /* A=number of days in month INC A INC (IX+6) /* increment day of month CP (IX+6) /* = nodays ?? RET NZ /* no so return LD (IX+6),1 /* yes so reset and contunue /* one month has passed LD A,13 INC (IX+7) /* increment months CP (IX+7) /* = 13 ?? RET NZ /* no so return LD (IX+7),1 /* yes so reset and contunue /* one year has passed LD A,100 INC (IX+8) /* increment years CP (IX+8) /* = 100 ?? RET NZ /* no so return LD (IX+8),0 /* yes so clear and contunue /* one century has passed INC (IX+9) /* increment century /* at this stage we stop, on the theory that this /* piece of software won't last to the year 9999 or 25599!!! RET /************************************************************** /* find the number of days in the month A nodays: PUSH HL CP 2 /* is month february ? JR Z,febday /* yes so special case -> febday DEC A LD B,0 LD C,A LD HL,mdays /* HL=address of table of days in months ADD HL,BC /* HL=address of month of interest LD A,(HL) /* A=days in month dayret: POP HL RET febday: LD A,(DE) /* (DE)=current year (eg 85) LD B,A LD A,0 CP B /* is it 0 (ie a century 1900, 2000, etc) LD A,28 JR Z,dayret /* yes so special case, not a leap year div4: SRL B /* no so divide by 2 JR CY,dayret /* if remainder then 28 days SRL B /* divide again by 2 JR CY,dayret /* if remainder then 28 days LD A,29 /* else leap year, so 29 days JR dayret /************************************************************** /* takes binary number in A and converts into its decimal parts /* returning least significant in E, next significant in D bindec: LD DE,0 bdloop: CP 10 JR CY,bdone INC D SUB 10 JR bdloop bdone: LD E,A RET /************************************************************** /* take the hours, minutes, and seconds and display /* them on the LEDs display:LD IX,leds /* address of temporary area for LEDs LD A,(secs) CALL bindec /* convert seconds into decimal parts LD (IX+0),E /* and load them LD (IX+1),D LD A,(mins) CALL bindec /* convert minutes into decimal parts LD A,16_10 OR E /* set bit 4 so that decimal point lit LD (IX+2),A /* and load them LD (IX+3),D LD A,(hour) CALL bindec /* convert hours into decimal parts LD A,16_10 OR E /* set bit 4 so that decimal point lit LD (IX+4),A /* and load them LD (IX+5),D CALL dis /* transfer scratch area to display RET /************************************************************** /* similar to bindec but convert the binary number into /* two ASCII digits in D,E binasc: CALL bindec LD A,16_30 ADD A,D LD D,A LD A,16_30 ADD A,E LD E,A RET /************************************************************** /* take the time and map it into COMPACT TIME then copy into /* the area which the control processor can read compac: LD HL,str1 /* address of first string scratch area LD A,(dmth) CALL com1 /* translate 'A' into ASCII and copy LD (HL),'/' /* day/month seperator LD A,(mnth) CALL com1 /* translate 'A' into ASCII and copy LD (HL),'/' /* month/year seperator LD A,(year) CALL com1 /* translate 'A' into ASCII and copy LD (HL),' ' /* two spaces seperate date from time INC HL LD (HL),' ' LD A,(hour) CALL com1 /* translate 'A' into ASCII and copy LD (HL),':' /* hour/minute seperator LD A,(mins) CALL com1 /* translate 'A' into ASCII and copy LD (HL),':' /* minute/second seperator LD A,(secs) CALL com1 /* translate 'A' into ASCII and copy LD (HL),'.' /* second/hundredth seperator LD A,(hund) CALL com1 /* translate 'A' into ASCII and copy LD HL,str1 LD BC,22 LD DE,RecB+4 CALL trans /* copy scratch area to common area RET /* finished com1: INC HL ful1: CALL binasc /* convert A to ASCII in D,E LD (HL),D /* copy D into (HL) INC HL LD (HL),E /* increment HL and fill with E INC HL RET /************************************************************** /* take the time and map it into FULL TIME then copy /* into the area which the control processor can read fullt: LD HL,str2 /* HL = start of scratch area for full time LD A,(dwek) LD DE,days /* start of look up table for days of weeks CALL cptxt /* copy from lookup table index A to (HL)+ LD A,(dmth) CALL binasc /* convert day of month into ASCII LD A,D CP '0' JR Z,fu1 /* don't print leading zero if any LD (HL),A INC HL fu1: LD (HL),E INC HL /* now have to append suffix st, nd etc. CP '1' JR Z,sufth /* number in teens (10 to 19) so th suffix LD A,E LD BC,16_7374 /* 1st CP '1' JR Z,sufix /* second digit '1' so 'st' LD BC,16_6E64 /* 2nd CP '2' JR Z,sufix /* second digit '2' so 'nd' LD BC,16_7264 /* 3rd CP '3' JR Z,sufix /* second digit '3' so 'rd' sufth: LD BC,16_7468 /* ?th sufix: LD (HL),B /* now copy suffix INC HL LD (HL),C INC HL LD (HL),' ' /* and add space at end LD A,(mnth) LD DE,months /* start of loopup table for months DEC A CALL cptxt /* copy from lookup table index A to (HL)+ LD A,(cent) CALL ful1 /* write in century LD A,(year) CALL ful1 /* write in year LD (HL),' ' INC HL /* and a couple of spaces LD (HL),' ' LD A,(hour) CALL com1 /* copy in hours LD (HL),':' /* write hours/minutes seperator LD A,(mins) CALL com1 /* copy in minutes LD (HL),':' /* write in minutes/seconds seperator LD A,(secs) CALL com1 /* copy in seconds LD (HL),'.' /* write in seconds/hundreds seperator LD A,(hund) CALL com1 /* copy in hundreds LD (HL),' ' LD A,(bstgmt) LD DE,daylit /* start of lookup table for BST/GMT CALL cptxt /* copy in appropriate string LD DE,str2 /* DE is start of scratch area SCF /* HL is now at end SBC HL,DE /* so subtract (with carry) to find length EX DE,HL /* HL now at start LD B,D LD C,E /* BC now equals length LD (HL),C /* copy string length into first byte INC C LD DE,RecC+4 CALL trans /* transfer lot to common area RET /************************************************************** /* copy appropriate string to (HL)+ /* string pointed to by A and found from lookup table at DE cptxt: INC HL EX DE,HL /* now DE is destination,HL is lookup table SLA A /* A = A*2 LD B,0 LD C,A ADD HL,BC /* find pointer from lookup table LD C,(HL) INC HL LD B,(HL) /* BC now equals address of string LD H,B LD L,C /* HL = BC LD B,0 LD C,(HL) /* BC = length of string to be copied INC HL LDIR /* Z80 block transfer EX DE,HL LD (HL),' ' /* put a space on at the end INC HL RET /* and return /************************************************************** /************************************************************** /* THE MSF ACQUSITION BIT, acquire data from serial input port acquir: LD HL,(sofar) INC HL LD (sofar),HL /* sofar = sofar + 1 LD A,(number) INC A LD (number),A /* number = number + 1 LD A,(input) /* sample the serial input port AND 1 /* Zflag = state of input LD HL,(state) /* current 'state' = address to jump to CALL mach /* call state machine LD (state),HL /* update with new 'state' RET /* and finished /************************************************************** /* start of state machine /* four states to 'iron out any noise'... a transition must /* stay in new state for two samples to register. mach: JP (HL) /* -> switch(state) S0: RET NZ LD HL,S1 /* %if value=0 %then state=1 RET S1: LD HL,S0 /* %if value#0 %then state=0 RET NZ LD A,(in) /* %else %start LD B,0 LD C,A LD HL,latest ADD HL,BC LD A,(number) LD (HL),A /* latest(in)=number CALL examin /* CALL examination to test for minute /* falling transition so examine data. LD A,(in) INC A LD (in),A /* in=(in+1)&255 LD A,0 LD (number),A /* number=0 LD HL,S2 /* state=2 RET /* %finish S2: RET Z LD HL,S3 /* %if value#0 %then state=3 RET S3: LD HL,S2 /* %if value=0 %then state=2 RET Z LD A,(in) /* %else %start LD B,0 LD C,A LD HL,latest ADD HL,BC LD A,(number) LD (HL),A /* latest(in)=number LD A,(in) INC A LD (in),A /* in=(in+1)&255 LD A,0 LD (number),A /* number=0 LD HL,S0 /* state=0 RET /* %finish /************************************************************** /* routine looks to see if 01111110 has been found /* if so then new minute examin: AND 255 /* clear carry flag LD HL,(sofar) LD BC,2000 SBC HL,BC RET CY /* %return %if sofar < 2000 /* go on only if we have enough data LD A,(in) SUB 15 LD E,A /* E{from}=in-15 LD D,0 LD B,8 /* %for i=1,1,8 %cycle LD IY,thold1 exloop: CALL getbit /* get the bit (0,1) from transition length RET Z /* %return %if bad data LD A,(frame) RLA /* shift in carry LD (frame),A /* frame=(frame<<1)!carry flag DJNZ exloop /* %repeat /* we now know the last 8 bits of data LD (last8),A CP 126 /* have we got framing code ?? RET NZ /* %return %if frame # 2_01111110 /************************************************************** /* now know that a new minute has just been detected LD HL,(sofar) LD BC,8600 SBC HL,BC JR CY,exret /* %return %if sofar < 8600 /* go on only if we have enough data LD A,(in) SUB 85 /* A{from}=in-85 CALL getime /* get data CALL ckpar /* check parity of data CALL NC,ckbcd /* check valid BCD & convert CALL NC,cktime /* check that time is valid CALL NC,range /* check time within range of current time? CALL NC,setime /* if all ok then set clock registers exret: LD HL,0 LD (sofar),HL /* reset number of interrupts to zero RET /************************************************************** /* acquire the time info from the MSF signal data built up /* start gathering from 'from' {in A} getime: LD IX,tyear /* start of temporary time store LD IY,thold1 /* use first level data decision threshold LD D,0 LD E,A /* DE = offset from base of buffer to start LD B,8 /* year information uses 8 bits LD (IX+0),0 /* clear current temporary year CALL shiftb /* get data for year INC IX LD B,5 /* month information uses 5 bits LD (IX+0),0 /* clear current temporary month CALL shiftb /* get data for month INC IX LD B,6 /* day of month information uses 6 bits LD (IX+0),0 /* clear current temporary day CALL shiftb /* get data for day of month INC IX LD B,3 /* day of week information uses 3 bits LD (IX+0),0 /* clear current temporary day CALL shiftb /* get data for day of week INC IX LD B,6 /* hour information uses 6 bits LD (IX+0),0 /* clear current temporary hour CALL shiftb /* get data for hour INC IX LD B,7 /* minute information uses 7 bits LD (IX+0),0 /* clear current temporary minute CALL shiftb /* get data for minute INC IX LD IY,thold2 /* parity etc comes in at second level LD B,8 /* and uses 8 bits LD (IX+0),0 /* clear current temporary parity stuff CALL shiftb /* get data for parity RET /************************************************************** /* shift in next B bits to location at IX shiftb: CALL getbit /* get the next bit JR Z,sbbad /* -> bad data %if Z flag set LD A,(IX+0) RLA /* shift in carry flag LD (IX+0),A /* save shifted data DJNZ shiftb /* B=B-1; %repeat %until B=0 RET sbbad1: CALL getbit sbbad: DJNZ sbbad1 /* skip rest of 'bits' LD (IX+0),255 /* mark data as bad RET /************************************************************** /* given last two transition length low/high return 1/0/invalid getbit: LD HL,latest /* HL = start of buffer, DE = offset within ADD HL,DE /* HL = address of interest LD C,(HL) /* C{ll}=latest(from),get low period length INC E /* from=from+1 LD HL,latest ADD HL,DE /* ditto for next byte LD A,(HL) /* A{hh}=latest(from),get high length INC E /* from=from+1 CP 180 JR NC,gbcnt /* -> continue %if hh > 180, special case ADD A,C /* A=hh+ll CP 180 /* period total must be greater than 900mS JR CY,gbbad /* -> bad data %if hh+ll < 180 CP 255 /* period total must be less than 1280mS JR NC,gbbad /* -> bad data %if hh+ll > 255 gbcnt: LD A,(IY+0) /* A = decision threshold to be used CP C /* ll > threshold ? JR Z,gbok /* -> data ok %if ll = threshold RET gbbad: AND 0 /* Z flag set, invalid data RET gbok: OR 255 /* Z flag reset, C flag reset, valid data RET /************************************************************** /* check that the acquired time data is valid parity ckpar: LD HL,tyear /* start of temporary time store LD IX,parity /* the address of parity information LD A,32 /* parity bit for year LD D,(HL) LD E,0 /* DE = data to be checked CALL ckpar1 /* check parity for year RET CY /* parity check failed LD A,16 /* parity bit for month & day INC HL LD D,(HL) INC HL LD E,(HL) /* DE = data to be checked CALL ckpar1 /* check parity for month & day RET CY /* parity check failed LD A,8 /* parity bit for day of week INC HL LD D,(HL) LD E,0 /* DE = data to be checked CALL ckpar1 /* check parity for day of week RET CY /* parity check failed LD A,4 /* parity bit for hour & minute INC HL LD D,(HL) INC HL LD E,(HL) /* DE = data to be checked CALL ckpar1 /* check parity for hours & minutes RET /************************************************************** /* Test a Word {DE} for parity /* IX = address of parity bits /* A = appropriate bit ckpar1: AND (IX+0) /* state of appropriate parity bit LD A,0 JR Z,parodd /* if not set then expect ODD OR D LD A,0 JP PE,pr2ev /* first even, so expect second even pr2od: OR E RET PO /* second odd so return OK SCF /* else invalid RET parodd: OR D LD A,0 JP PE,pr2od /* first even, so expect second odd pr2ev: OR E RET PE /* second even so return OK SCF /* else invalid RET /************************************************************** /* ckeck the data for valid BCD, and convert to Hex ckbcd: LD HL,tyear LD B,6 /* six numbers to be converted ckblop: CALL bcdbin /* check & convert JR NC,ckbbad /* Invalid BCD digit DJNZ ckblop /* test & convert all data SCF /* set carry (all ok) ckbbad: CCF RET /************************************************************** /* convert a BCD number (in (HL)) to hex number (in (HL)) bcdbin: LD A,15 AND (HL) /* A=least significant digit CP 10 RET NC /* -> invalid BCD digit LD D,A /* save it in D LD A,240 AND (HL) /* A=most significant digit CP 160 RET NC /* -> invalid BCD digit SRL A /* A=most significant * 8 LD E,A SRL E SRL E /* E=most significant * 2 ADD A,D ADD A,E /* A=result LD (HL),A /* replace BCD with hex INC HL SCF /* set carry flag to indicate OK RET /************************************************************** /* check that the time is valid /* return Carry set = invalid cktime: LD HL,tyear LD A,99 /* maximum valid year value CP (HL) RET CY /* invalid year INC HL LD A,0 /* minimum{-1} valid month CP (HL) JR Z,cktbad /* invalid month LD A,12 /* maximum valid month CP (HL) RET CY /* invalid month LD B,(HL) /* B = month INC HL LD A,0 /* minimum{-1} valid day of month CP (HL) JR Z,cktbad /* invalid day of month LD A,B /* month LD DE,tyear CALL nodays /* A = number of days in month CP (HL) RET CY /* invalid day of month INC HL LD A,6 /* maximum valid day of week CP (HL) RET CY /* invalid day of week INC HL LD A,23 /* maximum valid hour CP (HL) RET CY /* invalid hour INC HL LD A,59 /* maximum valid minute CP (HL) RET cktbad: SCF RET /************************************************************** /* check that the time is within +/- 1 minute of the time /* currently known to the clock, exception being if clock /* not reset yet range: LD A,(Rxed) CP 0 RET Z RET rngbad: SCF RET /************************************************************** /* set clock /* data has been checked as OK so copy from temporary area to /* the clocks internal registers setime: LD HL,tyear /* start of temporary time store LD A,(HL) /* A = year CP 85 LD A,20 /* century = 20 %if year < 85 JR CY,stcent LD A,19 /* %else century = 19 stcent: LD (cent),A /* deduce century LD DE,year /* DE = start of internal clock registers LD B,6 /* six numbers to copy stloop: LD A,(HL) /* copy them LD (DE),A INC HL DEC DE DJNZ stloop /* %repeat %until all six copied LD A,0 LD (secs),A /* reset seconds LD (hund),A /* and hundreds to zero INC A LD (twoh),A /* two hundreds to one LD A,(parity) AND 2 /* get BST/GMT bit JR Z,stpar LD A,1 stpar: LD (bstgmt),A /* set the BST/GMT bit as appropriate LD HL,tyear /* now note this time in LD BC,6 /* the 'last update' common area LD DE,RecD+4 /* record CALL trans LD A,-1 LD (Rxed),A /* indicate that time has now been set RET /************************************************************** /* data areas. mdays: 31; 28; 31; 30 /* number of days in each month, Feburary 31; 30; 31; 31 /* is of course a spacial case 30; 31; 30; 31 months: W jan /* lookup table for months, contains W feb /* the address of start of string W mar W apr W may W jun W jul W aug W sep W oct W nov W dece days: W sun /* lookup table for day of week, contains W mon /* the address of start of string W tue W wed W thu W fri W sat daylit: W gmt /* day light saving BST/GMT lookup table W bst /************************************************************** /* constant strings, first digit is the length Jan: 7 /* January 'J';'a';'n';'u' 'a';'r';'y' Feb: 8 /* Feburary 'F';'e';'b';'u' 'r';'a';'r';'y' Mar: 5 /* March 'M';'a';'r';'c' 'h' Apr: 5 /* April 'A';'p';'r';'i' 'l' May: 3 /* May 'M';'a';'y' Jun: 4 /* June 'J';'u';'n';'e' Jul: 4 /* july 'J';'u';'l';'y' Aug: 6 /* August 'A';'u';'g';'u' 's';'t' Sep: 9 /* September 'S';'e';'p';'t' 'e';'m';'b';'e' 'r' Oct: 7 /* October 'O';'c';'t';'o' 'b';'e';'r' Nov: 8 /* November 'N';'o';'v';'e' 'm';'b';'e';'r' Dece: 8 /* December 'D';'e';'c';'e' 'm';'b';'e';'r' Sun: 6 /* Sunday 'S';'u';'n';'d' 'a';'y' Mon: 6 /* Monday 'M';'o';'n';'d' 'a';'y' Tue: 7 /* Tuesday 'T';'u';'e';'s' 'd';'a';'y' Wed: 9 /* Wednesday 'W';'e';'d';'n' 'e';'s';'d';'a' 'y' Thu: 8 /* Thursday 'T';'h';'u';'r' 's';'d';'a';'y' Fri: 6 /* Friday 'F';'r';'i';'d' 'a';'y' Sat: 8 /* Saturday 'S';'a';'t';'u' 'r';'d';'a';'y' gmt: 3 /* GMT 'G';'M';'T' bst: 3 /* BST 'B';'S';'T' END