MediaWiki:Sitenotice:
2024-03-02: The wiki ran out of disk space, so things were not working. This has been resolved by adding another 5GB of quota ;-) Thanks to Tim Lindner for reporting the issues.
2020-05-17: If a page gives you an error about some revision not being found, just EDIT the page and the old page should appear in the editor. If it does, just SAVE that and the page should be restored. OS-9 Al (talk) 12:22, 17 May 2020 (CDT)
A Real-Time Talking Clock, Dennis Kitsz, page 5
A Real-Time Talking Clock
by DENNIS KITSZ
Part I
A real-time clock with voice synthesis!
What would you like? A real-time clock? A voice synthesizer? Info on using joysticks? Software without added hardware? A couple of hardware projects to dig into? Okay, get ready: I've got a something-for-everyone series beginning this month that includes two ways of producing voice from the Color Computer and two ways of adding a real-time clock. Depending on how you fold them together, you can have BASIC programs that speak or a clock that talks.
This month I'll be describing how to do voice synthesis - actually, voice recording" - in software, using your joystick input and cassette output ports. In the process, you will learn how the joystick input works, what sampling and timing mean, how the sound output works, and how you can force the high-speed POKE to work in RAM.
After you've had a chance to play with the vocal input/output, I'll create a purely software real-time clock. Since real time anything has to be totally independent of the vagaries of software, you'll learn about interrupts and video synchronization.
In the second part of this series I'll discuss interfacing and using the General Instrument vocal tract synthesizer (sold inexpensively by Radio Shack), and feeding it through the cartridge SND input. That will open the door to combining the vocal hardware with the interrupt-based realtime clock to create a talking timekeeper.
Finally, part three will offer an allophone (speech sound) vocal tract synthesizer that will give you an unlimited vocabulary. I'll provide it with inflection, too, and throw in one more twist to the plot - a battery-powered real-time clock. That will inevitably lead to a rewarding combination: a talking clock that keeps time even when the computer is off. It, too, will be interrupt-driven and completely transparent to other computer operations.
DIGITAL RECORDING
The basic principles of digital recording were explained in my article on Color Quaver (TCCM, November and December, 1983). Here's a review.
Sound is transmitted by variations in air pressure. Pressure variations are transformed by microphones into proportional electrical voltages. In analog recording (such as a phonograph record) the voltages are stored as wiggles in a record groove; in tape recording, .the voltages become variations In magnetic intensity. By reversing the process and forcing voltages through a loudspeaker, air pressure variations can be reproduced; sound results. At all of its stages, analog recording "looks" like the original sound in some respect.
Digital recording also transforms the air pressure changes into electrical variations, at least to start. From that point, though, the process differs dramatically. The electrical variations are measured at regular intervals, and the voltage that has been measured is stored in computer memory, or on disk or tape. To play the sound, the stored numbers are converted back to voltages at exactly the same rate that they were originally measured.
The process of measuring the changes in incoming voltage is called sampling, and the frequency of this process is called the sampling rate. The accuracy of the measurement is called its resolution. Sampling rate is expressed in kilohertz (thousands of cycles per second, abbreviated KHz), and resolution is provided by the number of bits used to sample and store the incoming voltage. The sampling rate affects the fidelity of the sound (faster sampling means a wider frequency range), and the resolution affects the level of sound intensity and noise (higher resolution means wider dynamic range and a better signal-tonoise ratio).
In the demonstration I've got for you, the sampling rate is about 3.6 KHz (7.2 KHz on a 64K computer using the high-speed RAM mode), and the resolution is six bits. The resulting frequency range ends at 1.8 KHz (3.6 KHz on a high-speed 64K computer), and the signal-to-noise ratio is a poor but quite intelligible 36 dB. High fidelity it ain't. Again, you'll find details in the articles on Color Quaver.
The Color Computer has all the means to record and play back voice or music. You won't get much of it - 4.5 seconds is as long as it will last in 16K of memory - but it will start you on adding voice to your Basic programs or doing experiments with digital sound recording.
THE JOYSTICK
The key to digital sampling is joystick input. Unlike simple Atari-style joysticks, which are made up of just four switches, Color Computer joysticks are variable resistors like stereo volume controls. A voltage (actually + 5 volts) is placeD on one side of the resistor, and ground is hooked to the other side. The variable resistor's "wiper" sweeps from the ground side to the + 5 volt side as you move the joystick, producing a variable voltage at the wiper contact. If the distance from ground to the wiper is labeled A and the distance from + 5 volts to the wiper is called B, then the voltage at the wiper is 5*(A/ A+B).
The Basic command PRINT JOYSTK(O) causes the computer to sample the voltage at the joystick wiper. The number displayed is 0 to 63, representing a range from 1/64ths of five volts (zero volts) to 63/64ths of five volts (4.92 volts). Try it; enter these lines:
10 CLS : A$="#.## VOLTS" 20 A=JOYSTK(O) 30 PRINT @ 269, USING A$; 5*A/64; 40 GOTO 20
As you move the joystick, the voltage being sampled (within 1/2 bit, or 1/128 volt) will be displayed.
The significant question is how the joysticks work. The voltage values don't just jump into the computer. And if you're looking for a sophisticated analog-to-digital converter chip, you won't find it.
The answer lies in Radio Shack's desire for a low-cost solution, and their cleverness in finding it. Converting a number to a voltage turns out to be an easier task than going the other way 'round. Converting a number to a proportional voltage goes this way: The binary value is fed through a group of resistors; each resistor contributes twice the previous amount of voltage (each bit to the right is twice the value of its neighbor to the left). Binary arithmetic prevails (I'm rushing here), and the resulting voltage ends up proportional to the original binary number. Figure 1 is the schematic of the digital-to-analog output stage of the Color Computer (the Color Computer 2 uses a custom integrated circuit for this).
If you can output a known voltage cheaply and easily, then you can measure any unknown voltage by comparing the two until you get a match. The known voltage feeds into one side of a commonplace electronic device known as a comparator; the unknown voltage feeds into the other side. The comparator flips from 0 to 1 when the unknown input equals or exceeds the known input. By reading the state of the comparator, the precise moment of match can be discovered. Figure 2 is the schematic of the joystick input section of the Color Computer.
Look at Listing 1, a Basic version of what Color Basic does when it checks the joystick. The digital-to-analog converter is found at address $FF20; the comparator bit is found at $FF00. The technique shown here is a binary search, also called successive approximation.
Control information is sent to the computer's ports (Lines 5 and 6) to get things set up and ready to read a joystick input. Variable A starts at the midway point (Line 7) and its value is sent to the binary-to-voltage converter (Line 10). Variable B receives the information from the comparator byte; bit 7 is the actual comparator result, and bits 0 through 6 contain other computer information. By ANDing value B with $80 (binary 10000000, Line 12), all but bit 7 are masked out, leaving the comparator information alone. Figure 3 shows that bit going into a 6821 peripheral interface adaptor, marked JOYIN.
If the comparator shows that the known outgoing value is less than the unknown incoming value, the binary search continues with a higher outgoing test value (Lines 14 - 16). Otherwise, it uses a lesser outgoing test value (Lines 17 - 18). The counter is decremented (Line 19), and the loop repeats until the process homes in on the unknown value. The result is divided by four to put it in the range 0 to 63, and then displayed.
Run the program. You'll see that - without ever using a JOYSTK command - the joystick value is printed correctly on the screen. Granted, the process is slow in Basic, but it demonstrates that analog-to-digital conversion is not mysterious.
If it works in Basic, it zips along in machine code. Look at Listing 2. The same values are sent to locations $FF01 and $FF03 to perform the set-up. The values 6 and $80 are stored in memory locations TEMP1 and TEMP2 as variable information. The value in the A accumulator goes to location DAC ($FF20).
The process varies just slightly, since a rotate command is available in 6809 commands, but not in Basic. Bit 7 of ADC ($FF00) is rotated into the carry flag; a branch-oncarry follows. The variable TEMP2 is divided by two (using a logical shift right, LSR). The rest of the listing is patterned after the Basic program. Here's an interesting comparison.
The Basic listing can do about three samples per second; using the JOYSTK command increases the sample to about 40 per second; the machine code routine can make over three and a half thousand samples each second.
DIGITAL RECORDER
So how does knowing how the joystick input works help you make a digital recording of your voice? Remember that the first stage of digital recording is the transformation of air pressure variations into proportional voltages. If the joystick input can measure joystick voltages, then shouldn't it follow that it can measure voltages from, say, a cassette player or microphone amplifier? Yes, it can. Basic can't do it because it's too slow. But machine code - at 3,600 samples per second-can pull in a reasonable representation of a voice.
Once you've got the voice stored as a series of numbers, reproducing it is easy. You swing through memory, taking each byte and feeding it to the digital-to-analog converter. The reproduced sound appears at the output. The only thing you need to keep in mind is that homing in on the sound sample takes longer than reproducing the sound by outputting a sample value...so you've got to add a little delay to keep the input and output rates matched.
Look at Listing 3, a complete record-reproduce program for the Color Computer. The heart of the joystick input program you've already seen is found in Lines 240520. The output routine is in 620 - 690. You can see that Lines 650 - 670 add the delay needed to keep input and output matched.
There are only a few other items to note. First of all, direct addressing is used to speed the program along (Lines 220 - 230). Next, interrupts are turned off to keep the sampling rate rock solid (Line 200). And finally, the "fast RAM" mode is used during the input and output sections (Line 210).
What is fast RAM? What are the mysterious high-speed POKEs all about? Here's the deal. The innards of your Color Computer are created from off-the-shelf components suited to many purposes. One of these is called the SAM (Synchronous Address Multiplexer). The SAM can support several computer configurations - that is, several combinations of types and sizes of RAM. The Color Computer uses several memory sizes (4K, 16K, 32K, 64K) but supports only one RAM type: dynamic.
Dynamic memory, through creative development and lots of luck, is low in power required and physically compact. Best of all, it is inexpensive. On the other hand, its special construction requires a continual refresh of its contents. This refresh is accomplished by reading 128 sequential memory locations at least every microsecond. The refresh complicates computer design because it has to be squeezed in between sequential instructions carried out by the central processing unit (CPU).
Designers at Motorola solved the problem by developing a combination of three sophisticated parts: the 6809 CPU itself, the SAM, and the 6847 video display generator. These work together to execute computer instructions, refresh the dynamic memory, and provide a video display.
Imagine the master clock of the Color Computer as a sequence of regular on-off pulses. During pulses 1, 3, 5, 7, 9 and so forth, the CPU executes its instructions from memory. During even-numbered pulses 2, 4, 6, 8 and so on, the memory is freed from the CPU to create the video display or to carry out the dynamic memory refresh. Since the video display generator also uses sequential addresses when it draws the screen, any video display has the effect of refreshing the memory.
When you turn your Color Computer on, it creates those alternating pulses - first one forthe CPU, then one for the video or refresh. It provides the pulses to all of memory. When you POKE 65495,0 (the usual so-called "high-speed" mode), you keep the refresh and video to the dynamic memory, but drop the extra pulse out when the computer uses the Basic ROM. Basic ROM is static and needs no refresh.
The "super high speed" mode is POKE 65497,0. If you try it, though, your video display will go into a frenzy, and you might lose your programs. That's because this POKE tells the SAM to bother with neither refresh nor video, but rather to consider the computer a machine with static RAM and no display output. No refresh, no video.
In this mode you might lose your programs because the refresh cycle is missing and the memory gradually "forgets" its contents. The mode conditions are summarized in Table 1.
But why does the super high speed work for program Listing 37 In this case, sequential memory is being filled with voice information, and then the voice information is played back. At this program's speed, the very act of filling and reading back the sequential memory acts as a memory refresh! The contents remain intact, undisturbed... though the video display is useless.
Once you've saved the source program in Listing 3 (w VOICE), you can assemble it to memory (A/IM/AO), quit the editor/assembler (Q), and protect Basic memory (CLEAR 200,&H3FOO). To use it, you'll need a joystick cable adaptor (see Figure 4). Turn the volume up on your television or monitor, pop a voice or music cassette into the recorder, and run the following lines:
10 EXEC &H3FOO 20 EXEC &H3F80 30 GOTO 10
The screen will go haywire, but the computer will record and play back the sound every two seconds. If you want to see the actual sound displayed as a sound wave, tap the Break key and enter and run Listing 4.
USING VOICE WITH BASIC
Since Basic programs have to run at normal speed to refresh the RAM, the voice you use with Basic won't have the quality you can hear at the higher speed. No matter; for some programs, any voice at all is better than none, so drop Lines 120, 130, 210, 550, 610 and 700 from Listing 3. You'll be able to store four seconds of sound in the 16K of memory above location $3FFF.
DIGITAL PUNCH IN THE NOSE
Now I'll turn away from digital voice recording to explore the first of two techniques for keeping the time of day. Keeping the" real time" is tricky because computer time is relative to its own master clock. The Color Computer's master clock (running at 894,886 pulses per second) is meaningful in just one way - you know exactly how much time each processor instruction takes. That's it; there's no easy way a program can know, so to speak, when one second has passed. Something has to knock on its door, interrupt its reverie, and punch it in the nose.
That digital punch is known as an interrupt. The Color Computer was built with interrupts in mind. The cursor flashes to the rhythm of interrupts, the Sound command counts them out to learn when it's done, and the timer runs with them. Fancy games depend on their presence.
Using interrupts isn't difficult, though it does call for a slight change of perspective. When you think of a program, you probably think of it as a logical series of commands followed one at a time without distraction. Interrupts seem to defy this concept, forcing the program away from its a ppoi nted tasks to work on someth i ng else.
That scenario is basically correct. The surprise is that the program in progress seems to have amnesia - it never remembers that it has been distracted from its work, can never recall what has happened during those lost microseconds.
The ideal interrupt happens like this: a signal (say, one synchronized with the video display) appears on the CPU's interrupt-request (IRQ) connection. If the machine code program in progress (such as the Basic interpreter) has enabledthe interrupt process, then this signal will be accepted as soon as the CPU is finished with its present instruction. The machine state (all the registers, program counter, and flags) gets stashed, and the program counter is given a new value. The new value in the program counter points to an "interrupt service routine."
The interrupt service routine takes care of whatever the programmer had in mind for that particular occurrence of the interrupt. When the interrupt service routine is finished, the original machine state (group of registers and flags) is restored and the main program continues exactly where it left off. A little time has mysteriously disappeared during the course of the main program.
A real-time clock is a perfect example of interrupt use.
VIDEO SYNCHRONIZATION
The Color Computer invites interrupt exploitation because both vertical and horizontal video signals can be used to generate interrupts. Television images require electrical pulses to cause each frame to begin at the top of the screen, and other pulses to cause each scanning line to begin at the left side of the screen. The signal to begin each frame is called vertical synchronization, and the signal to begin each line is called horizontal synchronization.
Vertical sync, which occurs 60 times every second, is an ideal candidate for interrupt use. After six pulses have occurred, then, 1/10 second has passed; after 60 pulses, one full second has passed. By keeping strict track of the pulse count, you can have a real-time clock.
Look at Listing 5, beginning at Line 200. At each interrupt pulse, the CPU will be directed there. Register X points to the final character in the clock image stored in memory, in ASCII, which starts at 00:00:00.00. The final digit is the 1/60 of a second counter. This value is incremented (Line 220), and checked to see if it has reached six. If it's less than six, the image is transferred to the screen by the exit routine beginning at Line 490. If the counter has reached six, it is reset to zero and the 1/10 of a second counter is incremented. If it hasn't reached ten, the routine exits; otherwise, the one-second counter is incremented. And so on, up to a total count of 99:59:59.95.
The exit routine transfers ten characters of the image to the screen. The 1/60 of a second dig it is not displayed.
The amazing thing about this 116-byte routine is that, as programs go, it is totally mundane. It's nothing more than increment, test, proceed, move memory, and exit. The only item to note is found at Line 570. The command JMP $894C lets Basic complete its part of the interrupt service routine chain (cursor, timer, sound) before returning to the program in progress.
Save the source code (W CLOCK), assemble this program into memory (A/IM/AO), quit the editor/assembler (Q), protect memory (CLEAR 200,&H3FOO) and execute the machine code program (EXEC &H3FOO). The clock will appear in the top corner of the screen, in reverse-video characters. This software clock will survive resets and CLOADs (not necessarily CLOADMs), and will run under most program conditions. Naturally, if you have a 32/64K machine, you'll wantto move it up in memory.
INTERRUPT THEORY
I passed by that interrupt theory pretty quickly to give you a chance to see it perform. There are some important things to understand before you put interrupts to work for yourself.
First of all, since interrupts come through at a regular pace, you've got to make sure you keep ahead of that pace. If you don't, you will not get back to the main course of your program properly.
Rule Number 1 is: make your interrupt routines short and efficient. When using the relatively laggard 60-persecond pulse of the vertical sync, that isn't much of a problem, because 1/60th of a second is 16,667 microseconds, or 14,915 Color Computer master clock pulses. The clock interrupt routine is lengthiest at the change back to 00 hours. Including the machine state saving at the interrupt itself, this requires 303 master pulses plus some more for Basic's timer, cursor and sound. The shortest execution for just a 1/60 second count is only 35 master clock pulses. Overall, the clock routine's time is minuscule with respect to the 14,915 master pulses available.
But what if you wanted to create a kind of super stopwatch? The Color Computer gives you a chance to do that, too. There isn't room to go into the details, but the horizontal synchronization from the video can also be used as an interrupt. This interrupt occurs 15,750 times each second, or once every 63.5 microseconds. This time, 63.5 microseconds is just over 56 computer clock cycles... hardly time to do much of anything. If you think 56 clock cycles isn't any time at all, though, turn to Steve Bjork's article on interrupts (Display Modes, December, 1983 TCCM); the pro speaks!
Figure 3 shows the schematic for interrupt handling in the Color Computer. Shown is the 6821 Peripheral Interface Adaptor (PIA). At the left the vertical (or field) synchronization is marked FS, and feeds CB1; horizontal synchronization is marked HS, and feeds CA 1. By proper programming (refer to the 6821 data sheet for details), either can be fed through to the CPU's interrupt request pin via IRQA and IRQB (right side of the PIA).
Oh yes. Here's Rule Number 2: if you are using interrupts in an independent program that does not work with the Basic ROMs at all, be sure to reset the interrupt latch by reading the correct PIA location (LDA $FF02). Otherwise, further interrupts won't be passed through the PIA, and you'll lose all the timing.
WHAT DOES IT ALL MEAN?
I won't tie all this together until later in this series, but keep in mind that all these concepts involve timing - the ultra-fast timing of digital recording, the hidden timing of interrupts, and (in the next part) the slow timing of interfacing with external electronics.
Ed's Note: Articles referred to, except Part I of this series, are found in back issues of The Color Computer Magazine, unless otherwise noted.
PROGRAM LISTING 1
BASIC emulation of a joystick input routine. 16K Extended Color BASIC.
1 CLS 2 X=&HFFOO '* PORT VALUE 3 DAC = &HFF20 '* D/A CONVT. 4 ADC = &HFFOO '* COMPARATOR 5 POKE &HFF01,&H34 '* CTRL. INFO 6 POKE &HFF03,&H37 '* CTRL. INFO 7 A = &H80 '* B 10000000 8 T1 = 6 '* ITERATIONS 9 T2 = &H80 '* B 10000000 10 POKE DAC,A '* SEND VALUE 11 B = PEEK (ADC) '* BYTE BIT 7 12 B = B AND &H80 '* TEST BIT 7 13 IF B = 0 THEN 17 '* IF FLIPPED 14 T2 = T2/2 '* ELSE LESS 15 A = A + T2 '* ADD VALUE 16 GOTO 19 '* GO TO NEXT 17 T2 = T2/2 '* IT IS MORE 18 A = A - T2 '* ELSE MORE 19 T1 = T1 - 1 '*'SUB. VALUE 20 IF T1>0 THEN 10 '* DONE YET? 21 PRINT@0,"JOY ="; '* A MESSAGE 22 PRINT INT(A/4) '* A JOYSTICK 23 GOTO 7 '* & DO AGAIN
PROGRAM LISTING 2
Machine code version of a joystick input routine, using a binary search.
00270 LDA #$34 00280 STA $FF01 00290 LDA #$37 00300 STA $FF03 00310 * 00320 NEXTIN LDA #$06 00330 STA <TEMP1 00340 LDA #$80 00350 STA <TEMP2 00360 * 00370 AGAIN STA DAC 00380 ROL ADC 00390 BCC BIGGER 00400 LESSER LSR <TEMP2 00410 ADDA <TEMP2 00420 BRA JUMP 00430 BIGGER LSR <TEMP2 00440 SUBA <TEMP2 00450 BRA JUMP 00460 JUMP DEC <TEMP1 00470 BNE AGAIN 00480 STA ,X+ 00490 *
PROGRAM LISTING 3
Continous record/reproduce program.
FF20 00100 DAC EQU $FF20 FF00 00110 ADC EQU $FF00 FFD9 00120 FAST EQU $FFD9 FFD8 00130 SLOW EQU $FFD8 4000 00140 MEMBOT EQU $4000 8000 00150 MEMTOP EQU $8000 00160 * 3F00 00170 ORG $3F00 3F 00180 SETDP $3F 00190 * 3F00 1A 50. 00200 START ORCC #$50 3F02 B7 FFD9 00210 STA FAST 3F05 86 3F 00220 LDA #$3F 3F07 IF 8B 00230 TFR A,DP 00240 * 3F09 8E 4000 00250 LDX #MEMBOT 00260 * 3F0C 86 34 00270 LDA #$34 3F0E B7 FF01 00280 STA $FF01 3F11 86 37 00290 LDA #$37 3F13 B7 FF03 00300 STA $FF03 00310 * 3F16 86 06 00320 NEXTIN LDA #$06 3F18 97 9B 00330 STA <TEMP1 3F1A 86 80 00340 LDA #$80 3F1C 97 9C 00350 STA <TEMP2 00360 * 3F1E B7 FF20 00370 AGAIN STA DAC 3F21 79 FF00 00380 ROL ADC 3F24 24 06 00390 BCC BIGGER 3F26 04 9C 00400 LESSER LSR <TEMP2 3F28 9B 9C 00410 ADDA <TEMP2 3F2A 20 06 00420 BRA JUMP 3F2C 04 9C 00430 BIGGER LSR <TEMP2 3F2E 90 9C 00440 SUBA <TEMP2 3F30 20 00 00450 BRA JUMP 3F32 0A 9B 00460 JUMP DEC <TEMP1 3F34 26 E8 00470 BNE AGAIN 3F36 A7 80 00480 STA ,X+ 00490 * 3F38 8C 8000 00500 CMPX #MEMTOP 3F3B 26 D9 00510 BNE NEXTIN 00520 * 3F3D 4F 00530 CLRA 3F3E IF 8B 00540 TFR A,DP 3F40 B7 FFD8 00550 STA SLOW 3F43 39 00560 RTS 00570 * 3F80 00580 ORG $3F80 00590 * 3F80 1A 50 00600 PLAYBK ORCC #$50 3F82 B7 FFD9 00610 STA FAST 3F85 8E 4000 00620 LDX #MEMBOT 3F88 A6 80 00630 SPEAK LDA ,X+ 3F8A B7 FF20 00640 STA DAC 3F8D C6 2C 00650 LDB #$2C 3F8F 5A 00660 LOOP DECB 3F90 26 FD 00670 BNE LOOP 3F92 8C 8000 00680 CMPX #MEMTOP 3F95 26 F1 00690 BNE SPEAK 3F97 B7 FFD8 00700 STA SLOW 3F9A 39 00710 RTS 00720 * 3F9B 00730 TEMP1 RMB 1 3F9C 00740 TEMP2 RMB 1 00750 * 0000 00760 00000 TOTAL ERRORS ADC FF00 MEMTOP 8000 AGAIN 3F1E NEXTIN 3F16 BIGGER 3F2C PLAYBK 3F80 DAC FF20 SLOW FFD8 FAST FFD9 SPEAK 3F88 JUMP 3F32 START 3F00 LESSER 3F26 TEMP1 3F9B LOOP 3F8F TEMP2 3F9C MEMBOT 4000
PROGRAM LISTING 4
Short BASIC routine to display the sound wave representation of the sound recorded by Listing 3. 32K Extended Color Basic
10 CLS0 20 FOR X = &H4000 TO &H8000 30 SET (Y, PEEK(X)/8, 5) 40 Y=Y+1: IF Y>63 THEN CLS0: Y=0 50 NEXT
PROGRAM LISTING 5
Real-time clock using Internal interrupts which occur 60 times each second. 32K Extended Color Basic
3FOO 00100 ORG $3FOO 00110 * 3FOO lA 50 00120 INTOFF ORCC #$50 * TURN INTERRUPTS OFF 3F02 8E 3FI0 00130 LDX #START * POINT X TO SERVICE ROUTINE 3F05 BF 010D 00140 STX $010D * STORE ROUTINE TO IRQ VECTOR 3F08 86 37 00150 LDA #$37 * VALUE 00110111 FOR MASKING 3FOA B7 FF03 00160 STA $FF03 * TURN ON VERTICAL SYNC 3FOD lC EF 00170 ANDCC #$EF * TURN INTERRUPTS ON 3FOF 39 00180 RTS * AND BACK TO BASIC "nK" 00190 * 3FI0 8E 3F77 00200 START LDX #IMAGE+I0 * POINT X TO 1/10 SEC. 3F13 C6 30 00210 LDB #$30 * B BECOMES ASCII OFFSET 3F15 6C 84 00220 INC ,x * INCREMENT 1/10 SECONDS 3F17 A6 84 00230 LDA ,x * GET 1/10 SECONDS VALUE 3F19 81 36 00240 CMPA #$36 * IS 6/10 SECONDS COUNTED? 3FIB 2D 2C 00250 BLT OUT * IF NOT 6/10 SECONDS, OUT 3F1D 8D 40 00260 BSR DECI * ELSE BAC UP 1 MEM. LOCATION 3FIF 81 3A 00270 CMPA #$3A * IS IT 1 SECOND YET? 3F21 2D 26 00280 BLT OUT * IF NOT 1 SECOND, OUT 3F23 8D 41 00290 BSR DEC2 * ELSE BACK UP 2 MEM. LOCNS. 3F25 81 3A 00300 CMPA #$3A * IS IT 10 SECONDS YET? 3F27 2D 20 00310 BLT OUT * IF NOT 10 SECONDS, OUT 3F29 8D 34 00320 BSR DECI * BACK UP 1 MEM. LOCATION 3F2B 81 36 00330 CMPA #$36 * IS IT 60 SECONDS YET? 3F2D 2D lA 00340 BLT OUT * IF NOT 60 SECONDS, OUT 3F2F 8D 35 00350 BSR DEC2 * ELSE BACK UP 2 MEM. LOCNS. 3F31 81 3A 00360 CMPA #$3A * IS IT 10 MINUTES YET? 3F33 2D 14 00370 BLT OUT * IF NOT 10 MINUTES, OUT 3F35 8D 28 00380 BSR DECI * ELSE BACK UP 1 MEM. LOCATION 3F37 81 36 00390 CMPA #$36 * IS IT 60 MINUTES YET? 3F39 2D DE 00400 BLT OUT * IF NOT 60 MINUTES, OUT 3F3B 8D 29 00410 BSR DEC2 * ELSE BACK UP 2 MEM. LOCNS. 3F3D 81 3A 00420 CMPA #$3A * IS IT 10 HOURS YET? 3F3F 2D 08 00430 BLT OUT * IF NOT 10 HOURS, OUT 3F41 8D 1C 00440 BSR DECI * ELSE BACK UP 1 MEM. LOCATION 3F43 81 3A 00450 CMPA #$3A * IS IT 100 HOURS YET? 3F45 2D 02 00460 BLT OUT * IF NOT 100 HOURS, OUT 3F47 E7 84 00470 STB ,x * PLACE $30 (ASCII ZERO) 00480 * 3F49 108E 0416 00490 OUT LDY #$0416 * POINT TO RIGHT SCREEN 3F4D 8E 3F6D 00500 LDX #IMAGE * POINT X TO CLOCK IMAGE 3F50 C6 OA 00510 LDB #$OA * COUNT 10 SCREEN POSITIONS 3F52 A6 80 00520 LOOP LDA ,X+ * GET CHARACTER FROM CLOCK 3F54 A7 AD 00530 STA ,Y+ * AND PLACE IT ON THE SCREEN 3F56 5A 00540 DECB * DONE WITH IMAGE YET? 3F57 26 F9 00550 BNE LOOP * IF NOT, THEN GET NEXT CHAR. 00560 * 3F59 B6 FF02 00570 LDA $FF02 * CLEAR VERT. SYNC LATCH 3F5C 7E 894C 00580 JMP $894C * AND TO BASIC TO DO RTI 00590 * 3F5F E7 84 00600 DECI STB ,X * PLACE $30 (ASCII ZERO) 3F61 6C 82 00610 INC ,-X * BACK UP ONE MEM. LOCATION 3F63 A6 84 00620 LDA ,X * GET VALUE FROM IMAGE 3F65 39 00630 RTS * BACK TO MAIN PROGRAM 00640 * 3F66 E7 84 00650 DEC2 STB ,X * PLACE $30 (ASCII ZERO) 3F68 6C 83 00660 INC ,--X * BACK UP TWO MEM. LOCATIONS 3F6A A6 84 00670 LDA ,X * GET VALUE FROM IMAGE 3F6C 39 00680 RTS * BACK TO MAIN PROGRAM 00690 * 3F6D 30 00700 IMAGE FCC /00:00:00.00/ 30 3A 30 30 3A 30 30 2c 30 30 00710 * 3FOO 00720 END INTOFF 00000 TOTAL ERRORS DECI 3F5F DEC2 3F66 IMAGE IF6D INTOFF 3FOO LOOP 3F52 OUT 3F49 START 3FI0