OSDN Git Service

modified: Project 16.bfproject
[proj16/16.git] / 16 / PCGPE10 / PIT.TXT
1          ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
2          ³ Programming the Intel 8253 Programmable Interval Timer ³\r
3          ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
4 \r
5               Written for the PC-GPE by Mark Feldman\r
6               e-mail address : u914097@student.canberra.edu.au\r
7                                myndale@cairo.anu.edu.au\r
8 \r
9                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
10                 ³      THIS FILE MAY NOT BE DISTRIBUTED     ³\r
11                 ³ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. ³\r
12                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
13 \r
14 \r
15 ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
16 ³ Disclaimer ³\r
17 ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
18 \r
19 I assume no responsibility whatsoever for any effect that this file, the\r
20 information contained therein or the use thereof has on you, your sanity,\r
21 computer, spouse, children, pets or anything else related to you or your\r
22 existance. No warranty is provided nor implied with this information.\r
23 \r
24 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
25 ³ Introduction ³\r
26 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
27 \r
28 The PIT chip has 3 channels, each of which are responsible for a different\r
29 task on the PC:\r
30 \r
31 Channel 0 is responsible for updating the system clock. It is usually\r
32 programmed to generate around 18.2 clock ticks a second. An interrupt 8 is\r
33 generated for every clock tick.\r
34 \r
35 Channel 1 controls DMA memory refreshing. DRAM is cheap, but it's memory\r
36 cells must be periodically refreshed or they quickly lose their charge. The\r
37 PIT chip is responsible for sending signals to the DMA chip to refresh\r
38 memory. Most machines are refreshed at a higher rate than neccesary, and\r
39 reprogramming channel 1 to refresh memory at a slower rate can sometime speed\r
40 up system performance. I got a 2.5 MHz speed-up when I did it to my 286, but\r
41 it didn't seem to work on my 486SUX33.\r
42 \r
43 Channel 2 is connected to the speaker. It's normally programmed to generate\r
44 a square wave so a continuous tone is heard. Reprogramming it for "Interrupt\r
45 on Terminal Count" mode is a nifty trick which can be used to play 8-bit\r
46 samples from the PC speaker.\r
47 \r
48 Each channel has a counter which counts down. The PIT input frequency is\r
49 1193181 ($1234DD) Hz. Each counter decrements once for every input clock\r
50 cycle. "Terminal Count", mentioned several times below, is when the counter\r
51 reaches 0.\r
52 \r
53 Loading the counters with 0 has the same effect as loading them with 10000h,\r
54 and is the highest count possible (approx 18.2 Hz).\r
55 \r
56 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
57 ³ The PIT Ports ³\r
58 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
59 \r
60 The PIT chip is hooked up to the Intel CPU through the following ports:\r
61 \r
62                 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
63                 ³ Port   Description                    ³\r
64                 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\r
65                 ³ 40h    Channel 0 counter (read/write) ³\r
66                 ³ 41h    Channel 1 counter (read/write) ³\r
67                 ³ 42h    Channel 2 counter (read/write) ³\r
68                 ³ 43h    Control Word (write only)      ³\r
69                 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
70 \r
71 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
72 ³ The Control Word ³\r
73 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
74 \r
75               ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿\r
76               ³ 7 ³ 6 ³ 5 ³ 4 ³ 3 ³ 2 ³ 1 ³ 0 ³\r
77               ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ\r
78                 ÀÄÂÄÙ   ÀÄÂÄÙ   ÀÄÄÄÂÄÄÄÙ   ÀÄÄ BCD 0 - Binary 16 bit\r
79                   ³       ³         ³               1 - BCD 4 decades\r
80 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄ¿  ³         ³\r
81 ³ Select Counter       ³  ³         ÀÄÄÄÄÄÄÄÄÄÄ Mode Number 0 - 5\r
82 ³ 0 - Select Counter 0 ³  ³\r
83 ³ 1 - Select Counter 1 ³  ³         ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
84 ³ 2 - Select Counter 2 ³  ³         ³ Read/Load                  ³\r
85 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ  ³         ³ 0 - Counter Latching       ³\r
86                           ÀÄÄÄÄÄÄÄÄÄ´ 1 - Read/Load LSB only     ³\r
87                                     ³ 2 - Read/Load MSB only     ³\r
88                                     ³ 3 - Read/Load LSB then MSB ³\r
89                                     ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
90 \r
91 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
92 ³ The PIT Modes ³\r
93 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
94 \r
95 The PIT is capable of operating in 6 different modes:\r
96 \r
97 MODE 0 - Interrupt on Terminal Count\r
98 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
99 When this mode is set the output will be low. Loading the count register\r
100 with a value will cause the output to remain low and the counter will start\r
101 counting down. When the counter reaches 0 the output will go high and remain\r
102 high until the counter is reprogrammed. The counter will continue to count\r
103 down after terminal count is reached. Writing a value to the count register\r
104 during counting will stop the counter, writing a second byte starts the\r
105 new count.\r
106 \r
107 MODE 1 - Programmable One-Shot\r
108 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
109 The output will go low once the counter has been loaded, and will go high\r
110 once terminal count has been reached. Once terminal count has been reached\r
111 it can be triggered again.\r
112 \r
113 MODE 2 - Rate Generator\r
114 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
115 A standard divide-by-N counter. The output will be low for one period of the\r
116 input clock then it will remain high for the time in the counter. This cycle\r
117 will keep repeating.\r
118 \r
119 MODE 3 - Square Wave Rate Generator\r
120 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
121 Similar to mode 2, except the ouput will remain high until one half of the\r
122 count has been completed and then low for the other half.\r
123 \r
124 MODE 4 - Software Triggered Strobe\r
125 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
126 After the mode is set the output will be high. Once the count is loaded it\r
127 will start counting, and will go low once terminal count is reached.\r
128 \r
129 MODE 5 - Hardware Triggered Strobe\r
130 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
131 Hardware triggered strobe. Similar to mode 5, but it waits for a hardware\r
132 trigger signal before starting to count.\r
133 \r
134 Modes 1 and 5 require the PIT gate pin to go high in order to start\r
135 counting. I'm not sure if this has been implemented in the PC.\r
136 \r
137 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
138 ³ Counter Latching ³\r
139 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
140 \r
141 Setting the Read/Load field in the Control Word to 0 (Counter Latch) causes\r
142 the appropriate channel to go into a sort of "lap" mode, the counter keeps\r
143 counting down internally but it appears to have stopped if you read it's\r
144 values through the channel's counter port. In this way you get a stable count\r
145 value when you read the counter. Once you send a counter latch command you\r
146 *must* then read the counter.\r
147 \r
148 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
149 ³ Doing Something Useful ³\r
150 ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
151 \r
152 Ok, so let's say we are writing a game and we need to have a certain\r
153 routine called 100 times a second and we want to use channel 0 to do all\r
154 this timing in the background while the main program is busy doing other\r
155 stuff.\r
156 \r
157 The first thing we have to realise is that BIOS usually uses channel 0 to\r
158 keep track of the time, so we have 3 options:\r
159 \r
160 1) Have our own routine handle all timer interrupts. This will effectively\r
161    stop the PC clock and the system time will be wrong from that point on.\r
162    The clock will be reset to the proper time the next time the computer\r
163    is turned off and on again, but it's not a nice thing to do to someone\r
164    unless you really have to.\r
165 \r
166 2) Have our routine do whatever it has to do and then call the BIOS handler.\r
167    This would be fine if our program was receiving the usual 18.2 ticks\r
168    a second, but we need 100 a second and calling the BIOS handler for every\r
169    tick will speed up the system time. Same net result as case 1.\r
170 \r
171 3) Have our routine do the interrupt handling and call the BIOS handler only\r
172    when it needs to be updated! BINGO!\r
173 \r
174 The PIT chip runs at a freqency of 1234DDh Hz, and normally the BIOS timer \r
175 interrupt handler is called for every 10000h cycles of this clock. First we \r
176 need to reprogram channel 0 to generate an interrupt 100 times a second, ie \r
177 every 1234DDh / 100 = 11931 cycles. The best thing to do is keep a running \r
178 total of the number of clock ticks which have occurred. For every interrupt \r
179 generated we will add 11931 to this total. When it reaches 10000h our handler \r
180 will know it's time to tell BIOS about it and do so.\r
181 \r
182 So let's get into some good old Pascal code. First we'll define a few\r
183 constants and variables our program will need:\r
184 \r
185 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
186 Uses Crt, Dos;\r
187 \r
188 {$F+} { Force far mode, a good idea when mucking around with interrupts }\r
189 \r
190 const TIMERINTR = 8;\r
191        PIT_FREQ = $1234DD;\r
192 \r
193 var BIOSTimerHandler : procedure;\r
194     clock_ticks, counter : longint;\r
195 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
196 \r
197 The clock_ticks variable will keep track of how many cycles the PIT has\r
198 had, it'll be intialised to 0. The counter variable will hold the new\r
199 channel 0 counter value. We'll also be adding this number to clock_ticks\r
200 every time our handler is called.\r
201 \r
202 Next we need to do some initialization:\r
203 \r
204 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
205 procedure SetTimer(TimerHandler : pointer; frequency : word);\r
206 begin\r
207 \r
208   { Do some initialization }\r
209   clock_ticks := 0;\r
210   counter := $1234DD div frequency;\r
211 \r
212   { Store the current BIOS handler and set up our own }\r
213   GetIntVec(TIMERINTR, @BIOSTimerHandler);\r
214   SetIntVec(TIMERINTR, TimerHandler);\r
215 \r
216   { Set the PIT channel 0 frequency }\r
217   Port[$43] := $34;\r
218   Port[$40] := counter mod 256;\r
219   Port[$40] := counter div 256;\r
220 end;\r
221 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
222 \r
223 Pretty straightforward stuff. We save the address of the BIOS handler,\r
224 install our own, set up the variables we'll use and program PIT channel 0\r
225 for the divide-by-N mode at the frequency we need.\r
226 \r
227 This next bit is what we need to do once our program is finished. It just\r
228 resets everything back to normal.\r
229 \r
230 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
231 procedure CleanUpTimer;\r
232 begin\r
233   { Restore the normal clock frequency }\r
234   Port[$43] := $34;\r
235   Port[$40] := 0;\r
236   Port[$40] := 0;\r
237 \r
238   { Restore the normal ticker handler }\r
239   SetIntVec(TIMERINTR, @BIOSTimerHandler);\r
240 end;\r
241 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
242 \r
243 \r
244 Ok, here's our actual handler. This particular handler just writes an\r
245 asterix (*) to the screen. Then it does the checks to see if the BIOS\r
246 handler should be called. If so it calls it, if not it acknowledges the\r
247 interrupt itself.\r
248 \r
249 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
250 \r
251 procedure Handler; Interrupt;\r
252 begin\r
253 \r
254   { DO WHATEVER WE WANT TO DO IN HERE }\r
255   Write('*');\r
256 \r
257   { Adjust the count of clock ticks }\r
258   clock_ticks := clock_ticks + counter;\r
259 \r
260   { Is it time for the BIOS handler to do it's thang? }\r
261   if clock_ticks >= $10000 then\r
262     begin\r
263 \r
264       { Yep! So adjust the count and call the BIOS handler }\r
265       clock_ticks := clock_ticks - $10000;\r
266 \r
267       asm pushf end;\r
268       BIOSTimerHandler;\r
269     end\r
270 \r
271   { If not then just acknowledge the interrupt }\r
272   else\r
273     Port[$20] := $20;\r
274 end;\r
275 \r
276 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
277 \r
278 And finally our calling program. What follows is just an example program\r
279 which sets everything up, waits for us to press a key and then cleans up\r
280 after itself.\r
281 \r
282 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r
283 begin\r
284   SetTimer(Addr(Handler), 100);\r
285   ReadKey;\r
286   CleanUpTimer;\r
287 end.\r
288 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
289 \r
290 \r
291 ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
292 ³ References ³\r
293 ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ\r
294 \r
295 Title : "Peripheral Components"\r
296 Publisher : Intel Corporation\r
297 ISBN : 1-55512-127-6\r