>LS=2     CLOCK-80 turns your TRS-80 into one of the world's most expensive digital clocks.  It is a good program to show off some of the computer's abilities, and is interesting to watch.  Also, it has several useful routines that can be incorporated into your own programs.     The assembly language program is written for the Radio Shack Model I or III, 16K to 48K, cassette or disk system. The Model I system requires an expansion interface for the interrupts.     The program works off of the interrupts. The interrupts on a Model I are different from a Model III. Since the program was intended to run on both computers, I incorporated a self-modifying code.  That means the program can tell on which computer it's running and change itself accordingly.     The program also uses a screen buffer for better graphics.  A lot of games use this technique.  When you draw things on the screen you have to erase what's there, and draw the new graphics.  That works, but you can get a flicker when you erase the screen.  Using a buffer for the screen eliminates this.  You erase the screen buffer, put what you want to display in it, and move the buffer onto the screen. This eliminates the flicker caused by erasing the screen, and thus you have better graphics.     The program starts its execution at line 120 (START). First it disables the interrupts so that when it changes the interrupt routine address the program will not crash. Then it sets the stack pointer to 7FFFH so I know where it is and that there will be plenty of room for the stack.     Lines 140-160 check to see on which computer it's running.  If it's on a Model III then the program jumps to line 510 (MODEL3) which is the start of the main program.     Lines 170-210 check to see if the computer has a lower case modification.  If it does not then lines 220-230 change the print routine to output UPPER CASE characters only.     Lines 240-460 create the Model I version.  First, it loads A with the ASCII code for an I, then line 150 puts this into the start-up message, which changes the 3 to an I for Model I users.  Next it loads HL with a zero for the op-codes NOP,NOP.  NOP stands for no operation.  Then lines 270-320 place the NOP's at ZAP1, ZAP2, ZAP3, ZAP4, ZAP5, ZAP6+2.  It does this because the Model I computer doesn't need the instructions at those addresses.     It then loads A with 3AH which is the op-code for LD  A,(memory location).  Lines 340-360 change the instructions at XIT, FDC, ZAP6.  The reason for this is that the Model I interrupts are memory mapped.  Next line 370 loads A with 37H which is the MSB of the interrupt address.  Then lines 380-400 change the MSB of the addresses at XIT+2, FDC+2, ZAP6+2.  After that it loads A with 0E0H which is the LSB of the interrupt address and changes the LSB of the address at ZAP6+1.  It did not change the LSB of the interrupt address at XIT+1 or FDC+1 because those bytes are already correct.     Lines 430-440 change the number of interrupts in a second.  It does this because the Model I has 40 interrupts per second, while the Model III has 30 interrupts per second.  Lines 450-460 change the number of interrupts for the half seconds.  This is for the blinking colons.     Lines 510-580 print the start-up message and get the time.  First it clears the screen, and prints the start-up message.  Then line 580 calls the input routine.     Lines 620-1010 do a syntax check on the input line.  It starts by loading HL with the address of the input buffer.  Then line 630 calls CHECKT which checks to see if the next two characters are digits, and jumps to the error routine if there are not.  Next line 650 checks to see if the next character is a colon, if not then line 660 jumps to the error routine.  Then it calls CHECKT, checks for a colon, and calls CHECKT again.  After that lines 740-800 check for a dash, a space, or a colon, which are all separators that can be used between the time and the AM/PM part.  Next, lines 810-1010 check the AM/PM part.  If the next character is an A then it sets the flag to zero, and line 890 jumps to line 960 (CHEKM).  If the character is not an A, then lines 900-930 check to see if the next character is a P.  If the character is not a P, then it jumps to the error routine, otherwise lines 940-950 set the flag to a one.  Last, lines 960-1010 check to see if the last character is an M.  If it is not then line 1010 jumps to the error routine.     Lines 1050-1230 convert the time from the ASCII string into the binary time.  It starts by loading HL with the address of the input buffer.  Next, line 1060 calls EVAL which converts two ASCII numbers into a one byte binary number, then lines 1070-1100 check to see if the hours are between 0-13.  If they are not, then line 1100 jumps to the error routine, otherwise line 1110 saves the hours.  Line 1120 moves HL over the colon to point to the minutes.  Next, lines 1130-1150 check to see if the minutes are less than 60.  If they are not, then line 1150 jumps to the error routine, otherwise line 1160 saves the minutes.  Line 1170 moves HL over the second colon to point to the seconds, then lines 1180-1200 check to see if the seconds are less than 60.  If they are not, then line 1200 jumps to the error routine, otherwise line 1210 saves the seconds.  Last, lines 1220-1230 reset the ticks.     Lines 1270-1370 set-up the interrupt routine.  First, line 1270 sets the interrupt mode to 1, which will make the processor go to the interrupt routine in the program.  Lines 1280-1300 reset the Model III interrupts, but on a Model I are changed to NOP's.  Lines 1310-1340 change the interrupt routine address to the one in the program, then line 1370 enable the interrupts to start the clock counter.     Lines 1410-1520 are the main program loop.  First, lines 1410-1420 check to see if the break key has been hit, if it has not then, line 1430 jumps to CONT, otherwise, lines 1440-1470 wait unit the break key has been released, and jumps back to MODEL3 which will get the time and start over.  When it does this you do not have to disable the interrupts because the interrupt routine address is already correct and it puts the same address back.  Line 1480 gets the ticks.  Next, line 1490 checks for a half second, if it is, then line 1500 calls the routine to erase the colons.  Line 1510 checks to see if the screen should be updated, if not then line 1520 loops back to the main program loop (LOOP).     Lines 1560-1620 convert the binary time back into an ASCII string.  It starts by loading IY with the input buffer address, then line 1570 puts the hours into A register.  Line 1580 calls GETTWO which converts the binary number in A register, puts this into the string that IY points to, and updates IY to point to the next ASCII number.  Line 1590 gets the minutes, line 1600 converts it, puts it into the string, then line 1610 gets the seconds, and line 1620 puts this into the string.  The program uses the input buffer to hold the ASCII time.     Lines 1660-1700 clear the screen buffer with a simple block move instruction.  First, lines 1660-1680 load HL with the first address in the buffer, load DE with the second address of the buffer, and load BC with the length of the buffer minus one, then line 1690 puts a blank graphic character at the first buffer address.  The LDIR is a block move instruction.  This moves the byte at the address HL to the address DE, increments HL and DE, decrements BC, and does this until BC equals zero.  This will put the 80H at all the spaces in the buffer.     Lines 1740-1890 display the clock in the screen buffer.  First, line 1740 loads IY with the input buffer,  then line 1750 loads HL with the address of the first digit in the screen buffer.  The DE register in line 1760 is loaded with 6 which is the offset used to point to the next character in the screen buffer.  Next, line 1770 loads B with 8, which is for the 8 characters to be printed, then line 1780 gets the ASCII value of the character to print in A register.  Next, line 1790 subtracts 2FH, which converts the character into its binary equivalent plus one.  Lines 1800-1820 save the HL, DE, and BC registers respectively,  then line 1830 calls the routine to display a digit in the A register at the screen position that HL points to.  Lines 1840-1860 restore the BC, DE, and HL registers.  Next, lines 1870 adds the offset to HL, which makes HL point to the address  of the next character.  Line 1880 adds one to IY so it will point to the next ASCII character to be displayed.  Line 1890 subtracts one from B, and jumps back to GETVAL if B is not zero.     Lines 1930-2010 display the AM/PM in the screen buffer.  Line 1930 loads DE with the address for AM/PM in the screen buffer, then line 1940 loads HL with the address of the AM message.  Next, lines 1950-1970 check the AM/PM flag.  If its zero, for AM then line 1970 jumps to PRINT which prints AM, otherwise lines 1980-2000 move HL to point to the PM message.  Line 2010 prints out the message that HL points to.     Lines 2050-2080 move the screen buffer to the screen with a block move instruction.  Finally, line 2090 jumps back to the program loop.     Lines 2130-2180 are the clear screen routine.  It is exactly like the clear buffer routine, except that it loads HL and DE with screen addresses instead of buffer addresses.     Lines 2220-2300 find out where the digit is.  First, line 2220 saves HL on the stack.  This is the address of the digit in the buffer.  Line 2230 loads HL with the start of the number data minus seven.  This will point to the right data after the offset is added.  Lines 2240-2250 load DE with seven which is the offset which will make HL point to the next digit data, and loads B register with the binary digit.  Lines 2260-2290 calculate where the digit data is, and put it in the IX register, then line 2300 gets the address of the character back into the HL register.     Lines 2310-2480 actually make the digit in the buffer.  The digits are 5 bytes wide and 7 bytes long.  Lines 2310-2320 load DE with the offset to point to the next line in the screen buffer, and load B with the number of lines to print.  Line 2330 (DISLOP) pushes the line counter onto the stack.  Next, line 2340 gets the row data for the digit in A register, then lines 2350-2380 shift A, so bit 7 will have the first bit to print, and load B register with the number of bits to print.  Lines 2390-2430 display the row by getting the next bit in the carry flag, and if it is set then it displays a graphic block, otherwise it just continues.  Line 2440 makes HL point to the next line, by adding the offset to HL.  Line 2450 increments the IX register, this makes IX point to the next row data.  Lines 2460-2470 pop the line count back into BC, and then the DJNZ instruction will subtract one from B and if it is not zero, then it will loop back to DISLOP.  If it is, then it falls through to line 2480 and returns.     Lines 2520-2650 are the subroutine that erase both colons.  First, line 2520 loads HL with the screen address of the first colon.  Line 2530 calls the routine to erase a character at HL.  Next, line 2540 loads HL with the address of the second colon.  Line 2550 (BLANK) loads the B register with the number of lines to erase.  Line 2560 pushes the line count onto the stack.  Line 2570 loads the B register with the number of bytes to erase on a line.  Lines 2580-2600 erase one line.  This puts a blank character at the address that HL points to.  It increments HL to point to the next address, and loops back to BLKLP2 until the whole line is erased.  Next, line 2610 gets the line count back from the stack.  Lines 2620-2630 load DE with the offset to make HL point to the beginning of the next line, and add it to HL; then lines 2640-2650 will loop back to BLKLP until all the lines are erased, otherwise return.     Lines 2700-2820 convert a binary number in A register into a two digit ASCII number.  Line 2700 loads B with -1, the B register is the tens counter.  Lines 2720-2730 subtract 10 from the A register, and will jump back to LOOPTW  if A is not less than zero.  What this does is divide by successive subtraction.  Lines 2740-2750 convert the least significant digit to ASCII and save it.  Lines 2760-2780 convert the most significant digit to ASCII and save it, then lines 2790-2820 move IY to point to the next number to be converted and return.     Lines 2860-2990 convert the ASCII number that HL points to, into a one byte binary number.  First, line 2860 calls GETDID the routine to get and ASCII digit and convert it to binary, then lines 2870-2910 multiply the first digit by 10.  This is done by getting the digit times 2, saving it, getting the digit times 8, and adding them together to get the digit times ten.  Line 2920 saves the most significant digit in C register.  Next, line 2930 calls GETDID to get the least significant digit, then lines 2940-2950 add the two digits together and return.  Lines 2960-2990 get an ASCII digit and convert it to binary.  Line 2960 gets the digit from where HL is pointing.  Line 2970 converts the ASCII digit to a binary digit, then lines 2980-2990 move HL to point to the next digit and return.     Lines 3030-3070 check two digits.  First, line 3030 calls CHECKO the routine to check for a legal digit.  Line 3040 moves HL to point to the next digit, then line 3050 calls CHECKO again for the second digit.  Lines 3060-3070 move HL to point to the next character, and return.     Lines 3110-3150 check to see if the character that HL point to is an ASCII digit, if it is, then it will return; otherwise, it will go to the error routine.  Line 3110 gets the ASCII digit to check.  Lines 3120-3130 jump to the error routine if the digit is less than an ASCII zero.  Line 3140 checks to see if the ASCII character is a digit, if it is then line 3150 returns, otherwise, it falls through to the error routine.     Lines 3190-3270 display the error message and jumps back to get the time again.  Lines 3190-3200 get rid of the return addresses from CHECKT and CHECKO by popping them off of the stack.  Whenever you make a call, the return address is pushed onto the stack.  Line 3210 loads HL with the address of the error message, and line 3220 loads DE with the address of the 5th line on the screen.  Next, line 3230 displays the error message, then lines 3240-3270 wait for the enter key to be hit, and jump back to get the time again.     Lines 3310-3500 are the print routines.  Line 3310 jumps to the print routine that displays both UPPER and lower case characters, this jump is changed to NOP,NOP if the computer does not have lower case.  Line 3320 (PRLOP) gets a character out of the string.  Next, lines 3330-3340  return if the end of the string is reached.  Next, lines 3350-3360 jump if the character is smaller than a lower case "a".  Lines 3370-3380 jump if the character is not lower case, otherwise, line 3390 converts the lower case character to UPPER CASE.  Line 3400 puts the character onto the screen, then lines 3410-3420 update the screen location and move HL to point to the next character to print.  Finally, line 3430 loops back to PRLOP which continues printing.  Lines 3440-3500 are the exact same routine, except that the lower case conversion is omitted.     Lines 3540-4300 are the input routine.  First, line 3540 loads B with the maximum number of characters to input.  Lines 3550-3560 load HL with the screen location for input, and load DE with the address of the input buffer.  Next, lines 3570-3580 save the BC, and HL registers.  Lines 3590-3610 display the line of periods on the screen, then lines 3620-3630 restore HL, and BC.  Line 3640 saves DE, then lines 3650-3680 clear the input buffer.  Line 3690 gets DE back from the stack, so DE will point to the start of the input buffer.  Line 3700 puts the cursor on the screen.  Lines 3710-3730 save the DE register and call the routine to get a character from the keyboard.  This routine returns the key that was pressed in the A register.  Lines 3740-3750 jump to ENTERC if the enter key was pressed.  Lines 3760-3770 jump to INPLS if a shifted backspace was hit.  Lines 3780-3790 jump to BACKSP if the backspace was pressed.  Lines 3800-3830 jump back to GETIHR if the character is less than 32, or greater than 122.  This filters out most of the unwanted characters.  Next, line 3840 copys the character that was pressed into the C register.  Lines 3850-3870 jump back to GETIHR if the buffer is full, otherwise, line 3880 adds one to the number of characters in the buffer.  Line 3890 copys the character that was pressed back into the A register.  Lines 3900-3910 put the character into the buffer and update the buffer pointer.  Lines 3920-3930 check to see if the computer can display lower case characters, then line 3940 gets the character back into the A register.  Line 3950 jumps to OPRNT if the computer can display lower case characters.  Lines 3960-3970 jump to OPRNT if the character is UPPER CASE, otherwise, line 3980 converts the lower case character to UPPER CASE.  Lines 3990-4000 put the character on the screen, then line 4010 jumps back to get another character.  Lines 4020-4040 jump to PEREXT if the input buffer is not full, otherwise, lines 4050-4060 will erase the cursor and return.  Line 4070 PEREXT will erase the cursor and lines 4080-4100 will put a blank character in the input buffer and return.  Lines 4110-4130 will jump back to GETIHR if there are no characters in the buffer and you try and backspace.  Lines 4140-4150 jump to PERD if the buffer is not full and you try and backspace.  Lines 4160-4180 erase the cursor and jump to BACKAL.  Lines 4190-4200 erase the cursor, and lines 4210-4220 erase the last character in the buffer.  Lines 4230-4250 subtract one from DE, and subtracts one from the number of characters in the buffer, and jumps back to the main input routine.  Lines 4260-4280 jump back to the main input routine if there are no characters in the buffer.  Lines 4290-4300 erase the cursor then jump back to the start of the input routine.     Lines 4340-5270 contain all of the data for the program.  Lines 4340-4430 are the messages that can be displayed.  Lines 4440-4490 are the variables for the program.  Lines 4500-5260 have the binary data for the big characters.  Last, line 5270 defines the area for the screen buffer.     Lines 5310-5810 are the interrupt routine.  First, it saves the HL, and AF registers on th stack.  Lines 5330-5340 clear the interrupt and read its status.  These two lines are changed to LD  A,(37E0H) and a NOP on a MODEL I computer.  Lines 5350-5360 jump to FDC if the interrupt was caused by the disk controller.  Lines 5370-5380 exit  if the interrupt is not valid.  Lines 5390-5410 add one to the ticks.  Lines 5420-5430 exit if it is not time to update the seconds, otherwise, it resets the ticks.  Next, line 5460 adds one to HL so it will point to the seconds and lines 5470-5480 add on to the seconds.  Lines 5490-5500 exit if it is not time to update the minutes, otherwise, it resets the seconds.  Next, line 5530 adds one to HL so it will point to the minutes and lines 5540-5550 add one to the minutes.  Lines 5560-5570 exit if it is not time to update the hours, otherwise, it resets the minutes.  Next, line 5600 adds one to HL so it will point to the hours and lines 5610-5620 add one to the hours.  Lines 5630-5640 jump if it is time to flip the AM/PM flag.  Lines 5650-5660 exit if the hour does not reset, otherwise, lines 5670-5690 set the hour to 1 and exit.  Lines 5700-5720 flip the AM/PM flag.  Lines 5730-5740 reset the interrupt.  These lines are replaced with LD  A,(37E0H) on a MODEL I computer.  Lines 5750-5760 restore the AF, and HL registers.  Next, lines 5770-5780 enable the interrupts and return.  Lines 5790-5820 reset the interrupt generated by the disk controller and continue.     The last line of the program tells the assembler the entry point of the program, which is labled START.     You could easily get the time from DOS, or change the program to get the time from an external clock.  Also, you might want to make it a military clock or add an alarm feature with sound out of the cassette port.  the program, which is labled START.     You could easily get the time from DOS, or change the program to get the time from an external clock.  Also, you might want to make it a military clock                                                               