Presentation is loading. Please wait.

Presentation is loading. Please wait.

Using the 8254 Timer-Counter Understanding the role of the system’s 8254 programmable Interval-Timer/Counter.

Similar presentations


Presentation on theme: "Using the 8254 Timer-Counter Understanding the role of the system’s 8254 programmable Interval-Timer/Counter."— Presentation transcript:

1 Using the 8254 Timer-Counter Understanding the role of the system’s 8254 programmable Interval-Timer/Counter

2 The 8254 PIT The 8254 Programmable Interval-timer is used by the PC system for (1) generating timer-tick interrupts (rate is 18.2 per sec), (2) performing dynamic memory-refresh (reads ram once every 15 microseconds), and (3) generates ‘beeps’ of PC speaker When the speaker-function isn’t needed, the 8254 is available for other purposes

3 Displaying ‘Time-Of-Day’ Algorithm steps: –Get the count of timer-interrupts so far today –Convert these ‘timer-ticks’ into seconds –Breakdown the total number of seconds today into Hours, Minutes, Seconds, and AM/PM –Convert numerical values into digit-strings –Output these results to the video terminal

4 Where’s the ‘tick’ counter? main memory Interrupt Vector Table (for real-mode) ROM-BIOS DATA AREA tick_count 0040:006C 0x00000 0x00400 0x00500 Number of timer-tick interrupts so far today (longword at 0x0046C)

5 Getting the ‘tick’ count The ROM-BIOS interrupt-handler for the timer interrupt stores the tick-count as a 32-bit integer located at address 0x046C (it’s in the ROM-BIOS DATA AREA) In real-mode, we can get it like this: xor%ax, %ax# address segment zero mov%ax, %fs# using FS register mov%fs:0x046C, %eax# copy tick-count to EAX mov%eax, total_ticks# save in a local variable segment-override prefix (segment used would be %ds)

6 Converting ‘ticks’ to seconds total_seconds_today = total_ticks_today number of ticks-per-second The number of ‘ticks-per-second’ is based upon the way the PC’s timing hardware has been programmed

7 Input/Output frequencies The input-pulses to each Timer-channel is a long established PC standard, based on the design of the chrystal oscillator chip: 1,193,182 pulses-per-second (Hertz) The frequency of the output-pulses from any Timer-channel is determined by how that channel’s Latch was programmed

8 Three timer/counter ‘channels’ Channel 0 Channel 1 Channel 2 8254 PIT 8284 PCLK +5 V CLK0 CLK1 CLK2 GATE0 GATE1 GATE2 OUT0 OUT1 OUT2 Interrupt IRQ0 DRAM refresh speaker Port 0x61, bit #0 Port 0x61, bit #1 AND Port 0x61, bit #5 Port 0x61, bit #4 1193182 Hz

9 Controlling timer-channel 2 r/o r/w 7 6 5 4 3 2 1 0 I/O port 0x61 (aka ‘Port_B’) OUT2 status OUT1 status SPKR control GATE2 control RAM parity error I/O channel error RAM parity checking enabled I/O channel checking enabled

10 Counter decrements when pulsed OUT CLK GATE LSB STATUS MSB LSBMSB LATCH REGISTER COUNT REGISTER TIMER/COUNTER CHANNEL

11 8254 Command-Port Channel-ID 00 = chn 0 01 = chn 1 10 = chn 2 Command-ID 00 = Latch 01 = LSB r/w 10 = MSB r/w 11 = LSB-MSB r/w Output Mode 000 = one-shot level 001 = retriggerable 010 = rate-generator 011 = square-wave 100 = software strobe 101 = hardware strobe Counting Mode 0 = binary 1 = BCD 7 6 5 4 3 2 1 0 CHANNELOUTPUT MODECOMMAND binary / BCD Commands are sent to the 8254 via io/port 0x43

12 Programming a PIT channel Step 1: send command to PIT (port 0x43) Step 2: read or write the channel’s Latch – via port 0x40 for channel 0 – via port 0x41 for channel 1 – via port 0x42 for channel 2

13 A ten-millisecond delay In future lessons we will want to create a time-delay of ten-milliseconds (allowing some hardware to finish its initialization) We can do it using the Timer Channel 2 We program its ‘Latch Register’ with the Timer Input-Pulse Frequency, multiplied by 1/100 (i.e., 1193182 / 100 = 11932) We specify the ‘one-shot’ counting mode

14 Code for the 10ms delay # enable Timer Channel 2 in$0x61, %al and$0x0C, %al or$0x01, %al out%al, $0x61 # program Channel 2 for “one-shot” countdown mov$0xB0, %al out%al, $0x43 # write Channel 2 Latch-Register (LSB/MSB) mov$11932, %ax out%al, $0x42 mov%ah, %al out%al, $0x42 # delay until OUT2 signal is activated (bit 5) poll:in$0x61, %al test$0x20, %al jzpoll # disable Timer Channel 2 in$0x61, %al and$0x0C, %al out%al, $0x61

15 Standard BIOS programming For Channel 0 (the ‘timer-tick’ interrupt) the Latch is programmed during system startup with a value of zero But the Timer interprets zero as 65,536 So the frequency of the output-pulses from Timer-channel 0 is equal to this quotient: output-frequency = input-frequency / frequency-divisor = 1193182 / 65536 (approximately 18.2)

16 Consequently… To compute ‘total_seconds’ from ‘total_ticks’: total_seconds = total_ticks / ticks_per_second = total_ticks / (1193182 / 65536) = ( total_ticks * 65536 ) / 1193183 We can use the x86 CPU’s integer-arithmetic instructions MUL (multiply) and DIV (divide)

17 How ‘MUL’ works EAX reg (or mem) EDXEAX 64-bit product multiplicand (32-bits) multiplier (32-bits) Before executing the MUL instruction… 32-bit operands After executing the MUL instruction… product (64-bits) mull reg_or_mem Here’s the instruction…

18 How ‘DIV’ works EDXEAX 64-bit dividend Before executing the DIV instruction… dividend (64-bits) reg (or mem) divisor (32-bits) 32-bit operand divl reg_or_mem Here’s the instruction… EDXEAX 32-bit remainder After executing the DIV instruction… two results (32-bits) 32-bit quotient

19 Implementing the conversion So use MUL and DIV to convert ‘ticks’ into ‘seconds’, like this: # total_seconds = ( total_ticks * FREQ_DIVISOR ) / PULSES_PER_SEC movtotal_ticks, %eax mov$FREQ_DIVISOR, %ecx mul%ecx mov$PULSES_PER_SEC, %ecx div%ecx mov%eax, total_seconds # Now integer-quotient is in EAX, and integer-remainder is in EDX

20 ‘Time-Of-Day’ Format HH:MM:SS am/pm hours minutes seconds morning or afternoon So we need to compute four numerical values from the ‘total_seconds’ integer

21 Our four time-parameters We use these arithmetical ideas: –total_minutes = ( total_seconds / 60 ); ss = ( total_seconds % 60 ); –total_hours = (total_minutes / 60 ); mm = ( total_minutes % 60 ); –total_halfdays = (total_hours / 12 ); hh = (total_hours % 12 ); –Total_days = ( total_halfdays / 2 ); xm = total_halfdays % 2;

22 A subtle refinement Our ‘total_seconds’ value was gotten with an integer-division operation, so there’s likely to be some ‘round-off’ error How can we be sure we use the ‘closest’ integer to the actual quotient? We should remember the ‘rounding’ rule! When ‘remainder’ is equal or greater than 1/2 of ‘divisor’, ‘quotient’ gets incremented

23 How to implement rounding? There is more than one way to do it – i.e., the “amateur’s” way or the “expert’s” way Knowledge of the CPU’s architecture and instruction-set can assist The ‘obvious’ method: if ( 2 * remainder >= divisor ) ++quotient; But this uses a multiply and a conditional jump-instruction (inefficient!)

24 Avoiding inefficiency… Replace the ‘multiply’ with an ‘addition’ Use ‘subtract’ and ‘add-with-carry’ instead of using ‘compare’ and ‘conditionally-jump’ # Recall: quotient was in EAX, remainder was in EDX, divisor was in ECX add%edx, %edx# doubles the remainder sub%ecx, %edx# computes: 2*quotient – divisor # now carry-flag is clear in case 2*quotient >= divisor cmc# complement the carry-flag bit # now carry-flag is set in case 2*quotient >= divisor adc$0, %eax# add the carry-flag to the quotient # So this achieves the same effect as the ‘rounding rule’, but wit no jump!

25 In-class exercise Can you enhance our ‘timeoday.s’ demo to make it more dramatic (and later useful) by creating a loop within its ‘main’ routine, so that it continues to read and display the time (until the user presses a key)? HINTS: Use an INT-0x16 keyboard service to ‘peek’ into the keyboard-queue, and omit the ‘\n’ (newline) control-code from the ‘report’ message-string


Download ppt "Using the 8254 Timer-Counter Understanding the role of the system’s 8254 programmable Interval-Timer/Counter."

Similar presentations


Ads by Google