Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2 | daniel-mar | 1 | unit HT46F47; |
2 | |||
3 | interface |
||
4 | |||
5 | type |
||
6 | Nibble = 0..15; |
||
7 | |||
8 | TRom = array[0..$FF] of byte; |
||
9 | |||
10 | THT46F47 = class(TObject) |
||
11 | strict private |
||
12 | procedure WaitMs(milliseconds: integer); |
||
13 | strict protected |
||
14 | // ROM |
||
15 | // Registers |
||
16 | FPC: byte; |
||
17 | FReturnAddress: byte; |
||
18 | FPage: Nibble; |
||
19 | FRegA: Nibble; |
||
20 | FRegB: Nibble; |
||
21 | FRegC: Nibble; |
||
22 | FRegD: Nibble; |
||
23 | FCalledOnce: boolean; |
||
24 | // In |
||
25 | FS1: boolean; |
||
26 | FS2: boolean; |
||
27 | FDIn: Nibble; |
||
28 | FAD1: Nibble; |
||
29 | FAD2: Nibble; |
||
30 | // Out |
||
31 | FPWM: Nibble; |
||
32 | FPortOut: Nibble; |
||
33 | public |
||
34 | // ROM |
||
35 | ROM: TRom; |
||
36 | property FROM: TRom read ROM; |
||
37 | // Registers |
||
38 | property PC: byte read FPC; |
||
39 | property ReturnAddress: byte read FReturnAddress; |
||
40 | property Page: Nibble read FPage; |
||
41 | property RegA: Nibble read FRegA; |
||
42 | property RegB: Nibble read FRegB; |
||
43 | property RegC: Nibble read FRegC; |
||
44 | property RegD: Nibble read FRegD; |
||
45 | // In |
||
46 | property S1: boolean read FS1 write FS1; |
||
47 | property S2: boolean read FS2 write FS2; |
||
48 | property DIn: Nibble read FDIn write FDIn; |
||
49 | property AD1: Nibble read FAD1 write FAD1; |
||
50 | property AD2: Nibble read FAD2 write FAD2; |
||
51 | // Out |
||
52 | property PWM: Nibble read FPWM; |
||
53 | property PortOut: Nibble read FPortOut; |
||
54 | // Functions |
||
55 | constructor Create; |
||
56 | procedure Step; |
||
57 | procedure Reset; |
||
58 | end; |
||
59 | |||
60 | implementation |
||
61 | |||
62 | uses |
||
63 | SysUtils, Forms; |
||
64 | |||
65 | { THT46F47 } |
||
66 | |||
67 | constructor THT46F47.Create; |
||
68 | var |
||
69 | i: integer; |
||
70 | begin |
||
71 | for i := Low(FROM) to High(FROM) do |
||
72 | begin |
||
73 | ROM[i] := $00; |
||
74 | end; |
||
75 | Reset; |
||
76 | end; |
||
77 | |||
78 | procedure THT46F47.Reset; |
||
79 | begin |
||
80 | // Internal |
||
81 | FPC := 0; |
||
82 | FReturnAddress := 0; |
||
83 | FPage := 0; |
||
84 | FRegA := 0; // Note: The registers don't seem to be reset in the actual chip, if a "soft reset" is done |
||
85 | FRegB := 0; |
||
86 | FRegC := 0; |
||
87 | FRegD := 0; |
||
88 | FCalledOnce := false; |
||
89 | // In |
||
90 | FS1 := false; |
||
91 | FS2 := false; |
||
92 | FDIn := 0; |
||
93 | FAD1 := 0; |
||
94 | FAD2 := 0; |
||
95 | // Out |
||
96 | FPWM := 0; |
||
97 | FPortOut := 0; |
||
98 | end; |
||
99 | |||
100 | procedure THT46F47.Step; |
||
101 | var |
||
102 | Instruction: byte; |
||
103 | ProgramCounterNext: Byte; |
||
104 | ProgramCounterCurrent: Byte; |
||
105 | begin |
||
106 | Instruction := FROM[FPC]; |
||
107 | |||
108 | ProgramCounterCurrent := FPC; |
||
109 | ProgramCounterNext := FPC+1; |
||
110 | |||
111 | try |
||
112 | (* |
||
113 | ---------------------------------------------------- |
||
114 | Code Assembly Alternative Comment |
||
115 | ---------------------------------------------------- |
||
116 | 10 LED 0000 PORT 0 |
||
117 | 11 LED 0001 PORT 1 |
||
118 | 12 LED 0010 PORT 2 |
||
119 | 13 LED 0011 PORT 3 |
||
120 | 14 LED 0100 PORT 4 |
||
121 | 15 LED 0101 PORT 5 |
||
122 | 16 LED 0110 PORT 6 |
||
123 | 17 LED 0111 PORT 7 |
||
124 | 18 LED 1000 PORT 8 |
||
125 | 19 LED 1001 PORT 9 |
||
126 | 1A LED 1010 PORT 10 |
||
127 | 1B LED 1011 PORT 11 |
||
128 | 1C LED 1100 PORT 12 |
||
129 | 1D LED 1101 PORT 13 |
||
130 | 1E LED 1110 PORT 14 |
||
131 | 1F LED 1111 PORT 15 |
||
132 | *) |
||
133 | if (Instruction >= $10) and (Instruction <= $1F) then |
||
134 | begin |
||
135 | FPortOut := Instruction and $F; |
||
136 | end; |
||
137 | |||
138 | (* |
||
139 | ---------------------------------------------------- |
||
140 | Code Assembly Alternative Comment |
||
141 | ---------------------------------------------------- |
||
142 | 20 WAIT 1ms |
||
143 | 21 WAIT 2ms |
||
144 | 22 WAIT 5ms |
||
145 | 23 WAIT 10ms |
||
146 | 24 WAIT 20ms |
||
147 | 25 WAIT 50ms |
||
148 | 26 WAIT 100ms |
||
149 | 27 WAIT 200ms |
||
150 | 28 WAIT 500ms |
||
151 | 29 WAIT 1s |
||
152 | 2A WAIT 2s |
||
153 | 2B WAIT 5s |
||
154 | 2C WAIT 10s |
||
155 | 2D WAIT 20s |
||
156 | 2E WAIT 30s |
||
157 | 2F WAIT 60s |
||
158 | *) |
||
159 | case Instruction of |
||
160 | $20: WaitMs(1); // WAIT 1ms |
||
161 | $21: WaitMs(2); // WAIT 2ms |
||
162 | $22: WaitMs(5); // WAIT 5ms |
||
163 | $23: WaitMs(10); // WAIT 10ms |
||
164 | $24: WaitMs(20); // WAIT 20ms |
||
165 | $25: WaitMs(50); // WAIT 50ms |
||
166 | $26: WaitMs(100); // WAIT 100ms |
||
167 | $27: WaitMs(200); // WAIT 200ms |
||
168 | $28: WaitMs(500); // WAIT 500ms |
||
169 | $29: WaitMs(1000); // WAIT 1s |
||
170 | $2A: WaitMs(2000); // WAIT 2s |
||
171 | $2B: WaitMs(5000); // WAIT 5s |
||
172 | $2C: WaitMs(10000); // WAIT 10s |
||
173 | $2D: WaitMs(20000); // WAIT 20s |
||
174 | $2E: WaitMs(30000); // WAIT 30s |
||
175 | $2F: WaitMs(60000); // WAIT 60s |
||
176 | end; |
||
177 | |||
178 | (* |
||
179 | ---------------------------------------------------- |
||
180 | Code Assembly Alternative Comment |
||
181 | ---------------------------------------------------- |
||
182 | 30 JUMP -0 HALT |
||
183 | 31 JUMP -1 |
||
184 | 32 JUMP -2 |
||
185 | 33 JUMP -3 |
||
186 | 34 JUMP -4 |
||
187 | 35 JUMP -5 |
||
188 | 36 JUMP -6 |
||
189 | 37 JUMP -7 |
||
190 | 38 JUMP -8 |
||
191 | 39 JUMP -9 |
||
192 | 3A JUMP -10 |
||
193 | 3B JUMP -11 |
||
194 | 3C JUMP -12 |
||
195 | 3D JUMP -13 |
||
196 | 3E JUMP -14 |
||
197 | 3F JUMP -15 |
||
198 | *) |
||
199 | if (Instruction >= $30) and (Instruction <= $3F) then |
||
200 | begin |
||
201 | ProgramCounterNext := ProgramCounterCurrent - (Instruction and $F); |
||
202 | end; |
||
203 | |||
204 | (* |
||
205 | ---------------------------------------------------- |
||
206 | Code Assembly Alternative Comment |
||
207 | ---------------------------------------------------- |
||
208 | 40 SET A=0 |
||
209 | 41 SET A=1 |
||
210 | 42 SET A=2 |
||
211 | 43 SET A=3 |
||
212 | 44 SET A=4 |
||
213 | 45 SET A=5 |
||
214 | 46 SET A=6 |
||
215 | 47 SET A=7 |
||
216 | 48 SET A=8 |
||
217 | 49 SET A=9 |
||
218 | 4A SET A=10 |
||
219 | 4B SET A=11 |
||
220 | 4C SET A=12 |
||
221 | 4D SET A=13 |
||
222 | 4E SET A=14 |
||
223 | 4F SET A=15 |
||
224 | *) |
||
225 | if (Instruction >= $40) and (Instruction <= $4F) then |
||
226 | begin |
||
227 | FRegA := Instruction and $F; |
||
228 | end; |
||
229 | |||
230 | (* |
||
231 | ---------------------------------------------------- |
||
232 | Code Assembly Alternative Comment |
||
233 | ---------------------------------------------------- |
||
234 | 50 |
||
235 | 51 SET B=A |
||
236 | 52 SET C=A |
||
237 | 53 SET D=A |
||
238 | 54 SET Dout=A |
||
239 | 55 SET Dout.0=A.0 |
||
240 | 56 SET Dout.1=A.0 |
||
241 | 57 SET Dout.2=A.0 |
||
242 | 58 SET Dout.3=A.0 |
||
243 | 59 SET PWM=A |
||
244 | 5A |
||
245 | 5B |
||
246 | 5C |
||
247 | 5D |
||
248 | 5E |
||
249 | 5F |
||
250 | *) |
||
251 | case Instruction of |
||
252 | $51: FRegB := FRegA; // SET B=A |
||
253 | $52: FRegC := FRegA; // SET C=A |
||
254 | $53: FRegD := FRegA; // SET D=A |
||
255 | $54: FPortOut := FRegA; // SET Dout=A |
||
256 | $55: FPortOut := ((FRegA and 1) shl 0) + (PortOut and 14); // SET Dout.0=A.0 |
||
257 | $56: FPortOut := ((FRegA and 1) shl 1) + (PortOut and 13); // SET Dout.1=A.0 |
||
258 | $57: FPortOut := ((FRegA and 1) shl 2) + (PortOut and 11); // SET Dout.2=A.0 |
||
259 | $58: FPortOut := ((FRegA and 1) shl 3) + (PortOut and 7); // SET Dout.3=A.0 |
||
260 | $59: FPWM := FRegA; // SET PWM=A |
||
261 | end; |
||
262 | |||
263 | (* |
||
264 | ---------------------------------------------------- |
||
265 | Code Assembly Alternative Comment |
||
266 | ---------------------------------------------------- |
||
267 | 60 |
||
268 | 61 SET A=B |
||
269 | 62 SET A=C |
||
270 | 63 SET A=D |
||
271 | 64 SET A=Din |
||
272 | 65 SET A=Din.0 |
||
273 | 66 SET A=Din.1 |
||
274 | 67 SET A=Din.2 |
||
275 | 68 SET A=Din.3 |
||
276 | 69 SET A=AD1 |
||
277 | 6A SET A=AD2 |
||
278 | 6B |
||
279 | 6C |
||
280 | 6D |
||
281 | 6E |
||
282 | 6F |
||
283 | *) |
||
284 | case Instruction of |
||
285 | $61: FRegA := FRegB; // SET A=B |
||
286 | $62: FRegA := FRegC; // SET A=C |
||
287 | $63: FRegA := FRegD; // SET A=D |
||
288 | $64: FRegA := DIn; // SET A=Din |
||
289 | $65: FRegA := (DIn and 1) shr 0; // SET A=Din.0 |
||
290 | $66: FRegA := (DIn and 2) shr 1; // SET A=Din.1 |
||
291 | $67: FRegA := (DIn and 4) shr 1; // SET A=Din.2 |
||
292 | $68: FRegA := (DIn and 8) shr 1; // SET A=Din.3 |
||
293 | $69: FRegA := AD1; |
||
294 | $6A: FRegA := AD2; |
||
295 | end; |
||
296 | |||
297 | (* |
||
298 | ---------------------------------------------------- |
||
299 | Code Assembly Alternative Comment |
||
300 | ---------------------------------------------------- |
||
301 | 70 |
||
302 | 71 SET A=A+1 |
||
303 | 72 SET A=A-1 |
||
304 | 73 SET A=A+B |
||
305 | 74 SET A=A-B |
||
306 | 75 SET A=A*B |
||
307 | 76 SET A=A/B |
||
308 | 77 SET A=A&B AND |
||
309 | 78 SET A=A|B OR |
||
310 | 79 SET A=A^B XOR |
||
311 | 7A SET A=~A NOT |
||
312 | 7B |
||
313 | 7C |
||
314 | 7D |
||
315 | 7E |
||
316 | 7F |
||
317 | *) |
||
318 | case Instruction of |
||
319 | $71: FRegA := FRegA + 1; // SET A=A+1 |
||
320 | $72: FRegA := FRegA - 1; // SET A=A-1 |
||
321 | $73: FRegA := FRegA + FRegB; // SET A=A+B |
||
322 | $74: FRegA := FRegA - FRegB; // SET A=A-B |
||
323 | $75: FRegA := FRegA * FRegB; // SET A=A*B |
||
324 | $76: begin |
||
325 | if FRegB = 0 then |
||
326 | FRegA := $F // this is the actual behavior of the microchip (program 40 51 45 76 54 30) |
||
327 | else |
||
328 | FRegA := FRegA div FRegB; // SET A=A/B |
||
329 | end; |
||
330 | $77: FRegA := FRegA and FRegB; // SET A=A&B AND |
||
331 | $78: FRegA := FRegA or FRegB; // SET A=A|B OR |
||
332 | $79: FRegA := FRegA xor FRegB; // SET A=A^B XOR |
||
333 | $7A: FRegA := not FRegA; // SET A=~A NOT |
||
334 | end; |
||
335 | |||
336 | (* |
||
337 | ---------------------------------------------------- |
||
338 | Code Assembly Alternative Comment |
||
339 | ---------------------------------------------------- |
||
340 | 80 PAGE 0 Page is used by JUMP, CALL, CTIMES and DTIMES |
||
341 | 81 PAGE 1 |
||
342 | 82 PAGE 2 |
||
343 | 83 PAGE 3 |
||
344 | 84 PAGE 4 |
||
345 | 85 PAGE 5 |
||
346 | 86 PAGE 6 |
||
347 | 87 PAGE 7 |
||
348 | 88 PAGE 8 |
||
349 | 89 PAGE 9 |
||
350 | 8A PAGE A |
||
351 | 8B PAGE B |
||
352 | 8C PAGE C |
||
353 | 8D PAGE D |
||
354 | 8E PAGE E |
||
355 | 8F PAGE F |
||
356 | *) |
||
357 | if (Instruction >= $80) and (Instruction <= $8F) then |
||
358 | begin |
||
359 | FPage := Instruction and $F; |
||
360 | end; |
||
361 | |||
362 | (* |
||
363 | ---------------------------------------------------- |
||
364 | Code Assembly Alternative Comment |
||
365 | ---------------------------------------------------- |
||
366 | 90 JUMP <PAGE>0 |
||
367 | 91 JUMP <PAGE>1 |
||
368 | 92 JUMP <PAGE>2 |
||
369 | 93 JUMP <PAGE>3 |
||
370 | 94 JUMP <PAGE>4 |
||
371 | 95 JUMP <PAGE>5 |
||
372 | 96 JUMP <PAGE>6 |
||
373 | 97 JUMP <PAGE>7 |
||
374 | 98 JUMP <PAGE>8 |
||
375 | 99 JUMP <PAGE>9 |
||
376 | 9A JUMP <PAGE>A |
||
377 | 9B JUMP <PAGE>B |
||
378 | 9C JUMP <PAGE>C |
||
379 | 9D JUMP <PAGE>D |
||
380 | 9E JUMP <PAGE>E |
||
381 | 9F JUMP <PAGE>F |
||
382 | *) |
||
383 | if (Instruction >= $90) and (Instruction <= $9F) then |
||
384 | begin |
||
385 | ProgramCounterNext := (FPage shl 4) + (Instruction and $F); |
||
386 | end; |
||
387 | |||
388 | (* |
||
389 | ---------------------------------------------------- |
||
390 | Code Assembly Alternative Comment |
||
391 | ---------------------------------------------------- |
||
392 | A0 CTIMES <PAGE>0 IF C>0 THEN ( C=C-1 ; JUMP <PAGE>X ) ELSE CONTINUE |
||
393 | A1 CTIMES <PAGE>1 |
||
394 | A2 CTIMES <PAGE>2 |
||
395 | A3 CTIMES <PAGE>3 |
||
396 | A4 CTIMES <PAGE>4 |
||
397 | A5 CTIMES <PAGE>5 |
||
398 | A6 CTIMES <PAGE>6 |
||
399 | A7 CTIMES <PAGE>7 |
||
400 | A8 CTIMES <PAGE>8 |
||
401 | A9 CTIMES <PAGE>9 |
||
402 | AA CTIMES <PAGE>A |
||
403 | AB CTIMES <PAGE>B |
||
404 | AC CTIMES <PAGE>C |
||
405 | AD CTIMES <PAGE>D |
||
406 | AE CTIMES <PAGE>E |
||
407 | AF CTIMES <PAGE>F |
||
408 | *) |
||
409 | if (Instruction >= $A0) and (Instruction <= $AF) then |
||
410 | begin |
||
411 | if FRegC > 0 then |
||
412 | begin |
||
413 | Dec(FRegC); |
||
414 | FReturnAddress := ProgramCounterCurrent; |
||
415 | ProgramCounterNext := (FPage shl 4) + (Instruction and $F); |
||
416 | end; |
||
417 | end; |
||
418 | |||
419 | (* |
||
420 | ---------------------------------------------------- |
||
421 | Code Assembly Alternative Comment |
||
422 | ---------------------------------------------------- |
||
423 | B0 DTIMES <PAGE>0 IF D>0 THEN ( D=D-1 ; JUMP <PAGE>X ) ELSE CONTINUE |
||
424 | B1 DTIMES <PAGE>1 |
||
425 | B2 DTIMES <PAGE>2 |
||
426 | B3 DTIMES <PAGE>3 |
||
427 | B4 DTIMES <PAGE>4 |
||
428 | B5 DTIMES <PAGE>5 |
||
429 | B6 DTIMES <PAGE>6 |
||
430 | B7 DTIMES <PAGE>7 |
||
431 | B8 DTIMES <PAGE>8 |
||
432 | B9 DTIMES <PAGE>9 |
||
433 | BA DTIMES <PAGE>A |
||
434 | BB DTIMES <PAGE>B |
||
435 | BC DTIMES <PAGE>C |
||
436 | BD DTIMES <PAGE>D |
||
437 | BE DTIMES <PAGE>E |
||
438 | BF DTIMES <PAGE>F |
||
439 | *) |
||
440 | if (Instruction >= $B0) and (Instruction <= $BF) then |
||
441 | begin |
||
442 | if FRegD > 0 then |
||
443 | begin |
||
444 | Dec(FRegD); |
||
445 | FReturnAddress := ProgramCounterCurrent; |
||
446 | ProgramCounterNext := (FPage shl 4) + (Instruction and $F); |
||
447 | end; |
||
448 | end; |
||
449 | |||
450 | (* |
||
451 | ---------------------------------------------------- |
||
452 | Code Assembly Alternative Comment |
||
453 | ---------------------------------------------------- |
||
454 | C0 |
||
455 | C1 SKIP_IF A>B |
||
456 | C2 SKIP_IF A<B |
||
457 | C3 SKIP_IF A=B |
||
458 | C4 SKIP_IF Din.0=1 |
||
459 | C5 SKIP_IF Din.1=1 |
||
460 | C6 SKIP_IF Din.2=1 |
||
461 | C7 SKIP_IF Din.3=1 |
||
462 | C8 SKIP_IF Din.0=0 |
||
463 | C9 SKIP_IF Din.1=0 |
||
464 | CA SKIP_IF Din.2=0 |
||
465 | CB SKIP_IF Din.3=0 |
||
466 | CC SKIP_IF S1=0 |
||
467 | CD SKIP_IF S2=0 |
||
468 | CE SKIP_IF S1=1 |
||
469 | CF SKIP_IF S2=1 |
||
470 | *) |
||
471 | case Instruction of |
||
472 | $C1: if FRegA > FRegB then Inc(ProgramCounterNext); |
||
473 | $C2: if FRegA < FRegB then Inc(ProgramCounterNext); |
||
474 | $C3: if FRegA = FRegB then Inc(ProgramCounterNext); |
||
475 | $C4: if ((Din and 1) shr 0) = 1 then Inc(ProgramCounterNext); |
||
476 | $C5: if ((Din and 2) shr 1) = 1 then Inc(ProgramCounterNext); |
||
477 | $C6: if ((Din and 4) shr 2) = 1 then Inc(ProgramCounterNext); |
||
478 | $C7: if ((Din and 8) shr 3) = 1 then Inc(ProgramCounterNext); |
||
479 | $C8: if ((Din and 1) shr 0) = 0 then Inc(ProgramCounterNext); |
||
480 | $C9: if ((Din and 2) shr 1) = 0 then Inc(ProgramCounterNext); |
||
481 | $CA: if ((Din and 4) shr 2) = 0 then Inc(ProgramCounterNext); |
||
482 | $CB: if ((Din and 8) shr 3) = 0 then Inc(ProgramCounterNext); |
||
483 | $CC: if S1 = false then Inc(ProgramCounterNext); |
||
484 | $CD: if S2 = false then Inc(ProgramCounterNext); |
||
485 | $CE: if S1 = true then Inc(ProgramCounterNext); |
||
486 | $CF: if S2 = true then Inc(ProgramCounterNext); |
||
487 | end; |
||
488 | |||
489 | (* |
||
490 | ---------------------------------------------------- |
||
491 | Code Assembly Alternative Comment |
||
492 | ---------------------------------------------------- |
||
493 | D0 CALL <PAGE>0 |
||
494 | D1 CALL <PAGE>1 |
||
495 | D2 CALL <PAGE>2 |
||
496 | D3 CALL <PAGE>3 |
||
497 | D4 CALL <PAGE>4 |
||
498 | D5 CALL <PAGE>5 |
||
499 | D6 CALL <PAGE>6 |
||
500 | D7 CALL <PAGE>7 |
||
501 | D8 CALL <PAGE>8 |
||
502 | D9 CALL <PAGE>9 |
||
503 | DA CALL <PAGE>A |
||
504 | DB CALL <PAGE>B |
||
505 | DC CALL <PAGE>C |
||
506 | DD CALL <PAGE>D |
||
507 | DE CALL <PAGE>E |
||
508 | DF CALL <PAGE>F |
||
509 | *) |
||
510 | if (Instruction >= $D0) and (Instruction <= $DF) then |
||
511 | begin |
||
512 | FCalledOnce := true; |
||
513 | FReturnAddress := ProgramCounterNext; |
||
514 | ProgramCounterNext := (FPage shl 4) + (Instruction and $F) |
||
515 | end; |
||
516 | |||
517 | (* |
||
518 | ---------------------------------------------------- |
||
519 | Code Assembly Alternative Comment |
||
520 | ---------------------------------------------------- |
||
521 | E0 RET |
||
522 | *) |
||
523 | if Instruction = $E0 then |
||
524 | begin |
||
525 | if not FCalledOnce then |
||
526 | begin |
||
527 | // "Freeze" is the behavior of the actual chip (see test program 41 54 27 42 54 27 E0 7A 27 54 33) |
||
528 | ProgramCounterNext := ProgramCounterCurrent; |
||
529 | end |
||
530 | else |
||
531 | begin |
||
532 | ProgramCounterNext := FReturnAddress; |
||
533 | end; |
||
534 | end; |
||
535 | finally |
||
536 | FPC := ProgramCounterNext; |
||
537 | end; |
||
538 | end; |
||
539 | |||
540 | procedure THT46F47.WaitMs(milliseconds: integer); |
||
541 | var |
||
542 | i: integer; |
||
543 | begin |
||
544 | for i := 0 to milliseconds div 10 do |
||
545 | begin |
||
546 | Sleep(10); |
||
547 | Application.ProcessMessages; |
||
548 | if Application.Terminated then break; |
||
549 | end; |
||
550 | end; |
||
551 | |||
552 | end. |