- Trước hết hãy xem bản chất của câu báo lỗi bằng tiếng anh đã!
(Tiếng Anh của tui phọt phẹt nhưng tạm dịch nó ra là biểu thức chia cho số 0)
Vậy thì trước hết bạn xem lại chương trình của mình xem xem có đoạn lệnh nào ?? đoạn biểu thức nào có các phép chia rồi v.v.. liên quan đến số 0 không thì kiểm tra khắc phục.
+ Tiếp theo mới là điều đáng nói đó là trong chương trình của bạn chẳng có tí tẹo nào về chia cho 0 cả mà vẫn lỗi như bình thường hoặc cùng lắm là chỉ có đoạn lệnh này cũng lỗi
uses crt;
begin
end.
Vậy tại sao lỗi ???? có lệnh nào đâu?? cũng chẳng có biểu thức nào cả cũng đã báo lỗi rồi! Lỗi ở đây không phải do chương trình của bạn mà nó đã nằm trong thư viên phần mềm Crt của turbo pascal của bạn rồi. Vân đề này là gì vậy tức là thư viện Crt này nó không tương thích với máy của bạn tức là khả năng xảy ra như sau:
máy của bạn thì quá tốt (thương thường bây giờ tốc độ máy là >500MHz) nhưng phiên bản turbo pascal thì quá cũ nên thư viện Crt không đáp ứng được nên lỗi.
Việc sửa lỗi thật đơn giản như đã giới thiệu ở bài trên chỉ cần download file turbo.tpl tương thích về là được
tiếc là diễn đàn đang không cho upload file lên rồi
các bạn tạm theo link này để download vậy
__________________ ko the nhin thay vi anh ay qua dep trai
đơn giản là bạn đã dùng lệnh "use crt;" trong khi hoặc máy bạn đang chạy TP 7.0 ko có unit crt hoặc là chip dùng trên nền celeron trở lên
Trích:
Trong thời gian qua, toà soạn đã nhận được rất nhiều thắc mắc của bạn đọc về vấn đề lỗi khi chạy Borland Turbo Pascal 7.0 trên các máy tính tốc độ cao (Pentium II, AMD K6,...). Borland (nay đổi tên thành Inprise) lại không có giải pháp chính thức cho vấn đề này mà người dùng phải "ráng tự lo lấy". Bạn có thể lên Internet lấy "trọn gói" những patch file màạ người dùng trên toàn thế giới "đóng góp" về dùng và không cần quan tâm cách làm. Trong trường hợp muốn tìm hiểu và "tự sản xuất" thì mời bạn xắn tay lên!
Như một số bạn sử dụng Borland Pascal 7.0 đã nhận xét trong số báo PCWorld tháng 3 năm 1999, với các bộ xử lý Intel Pentium Celeron, một số chương trình Pascal sau khi dịch và cho thực thi thì nhận được thông báo lỗi: Error 200: division by zero. Nếu đem đúng chương trình source cho dịch lại với Turbo Pascal 5.5 thì lại không có vấn đề gì xảy ra.
Điều khá đau lòng cho chúng ta là Borland, công ty tạo ra trình biên dịch nổi tiếng Turbo Pascal, sau một thời gian hoạt động thua lỗ đã quyết định đổi tên thành Inprise Inc. và bỏ rơi một số phần mềm của mình trong đó có Turbo Pascal. Version cuối cùng của Pascal do Borland đưa ra là 7.0. Việc cập nhật lên version mới hơn là điều không thực hiện được.
Là sinh viên của giai đoạn đầu thập kỷ 90, tôi đã khá viết khá nhiều chương trình bằng Turbo Pascal. Cách đây vài tháng, khi nâng cấp máy tính của mình lên Pentium Celeron 333, tôi đã nhận được thông báo lỗi tương tự như trên. Với một số bạn, nếu Turbo Pascal từ chối chạy với Celeron, viết lại chương trình bằng một ngôn ngữ khác có lẽ cũng chẳng thành vấn đề. Tuy nhiên, trong trường hợp của tôi, thời gian để dịch số source đã viết sang một ngôn ngữ khác như C hay C++ sẽ rất lớn. Chính vì vậy, tôi quyết định tìm hiểu nguyên nhân và cách khắc phục vấn đề trên. Trong bài viết này, xin chia sẻ kinh nghiệm của tôi cùng các bạn. Nếu không quan tâm tới nguyên nhân xâu xa của thông báo lỗi trên, bạn có thể bỏ qua mục Nguyên Nhân và đi thẳng tới phần Cách giải quyết.
Trái lại, nếu là người yêu thích ngôn ngữ Pascal và muốn đi sâu vào ngôn ngữ này, xin bỏ một chút thời gian cho mục Nguyên nhân, nơi mọi nguyên căn của vấn đề trên được trình bày.
Nguyên nhân
Trước hết, để có thể tìm được vị trí gây nên lỗi, chúng ta hãy dùng đoạn chương trình đơn giản sau:
Program Test;
Uses Crt;
Begin
Writeln('Hello');
End.
Rõ ràng, chương trình này chỉ viết chữ Hello lên màn hình. Khi các bạn chạy chương trình sẽ nhận được thông báo lỗi: Runtime error 200 at ****:0091. **** là địa chỉ segment gây lỗi, có thể khác với mỗi máy tùy theo các chương trình đã nạp trong bộ nhớ. 0091 là offset của đoạn mã lệnh gây nên lỗi.
Nếu bạn bỏ dòng thứ 2: Uses Crt thì chương trình lại chạy một cách "ngon lành". Sở dĩ chúng ta có thể bỏ dòng này là vì hàm writeln duy nhất dùng trong chương trình vừa thuộc unit SYSTEM ngầm định, vừa thuộc unit CRT. Trong trường hợp dùng lệnh Uses CRT, CRT.Writeln sẽ được gọi. Trái lại, SYSTEM.Writeln sẽ được sử dụng nếu bạn không viết Uses CRT. Vậy, lỗi có liên quan tới unit CRT.
Giữ nguyên dòng Uses CRT, và thêm dòng:
SYSTEM.writeln('System Hello')
vào trước dòng writeln('Hello'). Khi chạy chương trình, bạn cũng sẽ chẳng thấy dòng System Hello hiện ra, mà hệ thống vẫn đưa ra câu báo lỗi tương tự. Như vậy, phần mã gây lỗi nằm trước mã lệnh SYSTEM.Writeln(System Hello).
Từ 2 nhận xét trên, có thể phán đoán ngay phần mã gây lỗi là phần khởi động của unit CRT-phần được thực hiện đầu tiên khi chương trình Test được nạp vào bộ nhớ.
Nếu bạn dịch chương trình Test ra file EXE rồi tiến hành thi hành ngoài DOS thì sẽ nhận được thêm một chút thông tin về địa chỉ gây lỗi: Runtime error 200 at ****:0091. Error 200 theo Borland Pascal Help là lỗi Divide by zero (chia cho 0). Chương trình của chúng ta có cộng trừ nhân chia gì đâu mà gây nên lỗi này?
Như các bạn cũng biết, nếu dùng một unit nào đó, khi bắt đầu chạy chương trình, phần mã khởi động của Unit sẽ được thực hiện. Bằng cách sử dụng Turbo Debugger (tại dấu nhắc của Dos, nhập TD TEST), bạn có thể quan sát dễ dàng quan sát được các mã khởi động của unit SYSTEM và CRT vào đầu chương trình dưới dạng các lệnh Call Far:
CALL ****:0000 ; Khởi động unit system
CALL YYYY:000D ; Khởi động unit Crt
Trong Turbo Debugger, ấn F8 (Step Over) để thực hiện 1 lần lệnh thứ nhất và F7 (Trace) để bước vào chạy từng bước lệnh CALL thứ hai-lệnh khởi động CRT. Sau đó, chọn mục Run / Execute To, nhập offset địa chỉ cần tới là 0091 (hex), bấm Enter. Con trỏ sẽ hiện lên ở ngay trước vị trí đoạn mã lệnh gây lỗi( ****:0091). Nếu nhấn F8 1 lần nữa, Deburger sẽ hiện ra thông báo: Terminated, exit code 200. Để tiện theo dõi, tôi xin trích ra một phần các lệnh có liên quan phía trước và sau lệnh gây lỗi:
CRT_Initialize:
...............................
****:0071: MOV ES,Seg0040
****:0075: MOV DI,OFFSET Timer
****:0078: MOV BL,ES:[DI] ; ES = 0040h, DI = 006Ch
****:007b: @@2: CMP BL,ES:[DI] ; ESI = System clocktick
****:007e:JE @@2
****:0080: MOV BL,ES:[DI]
****:0083: MOV AX,-28
****:0086: CWD ; DX = FFFFh, AX = FFE4h
****:0087: CALL DelayLoop
****:008a: NOT AX
****:008c: NOT DX
****:008e: MOV CX,55
****:0091: DIV CX ; Nỳi gờy lửợi
****:0093: MOV DelayCount,AX
DelayLoop:
****:02c6: @@1: SUB AX,1
****:02c9: SBB DX,0
****:02cc: JC @@2
****:02ce: CMP BL,ES:[DI]
****:02d1: JE @@1
****:02d3: @@2: RET
Tôi không muốn bạn "sa lầy" vào hợp ngữ, xin giải thích ý chính của đoạn chương trình này như sau:
Khi phần khởi động của CRT chạy, chương trình sẽ khởi tạo một biến gọi là DelayCount (dạng word 2- byte). Biến này chứa số lần thực hiện các lệnh trong vòng lặp DelayLoop để làm chậm 1 mili giây. Nếu bạn cần delay N mili giây, hệ thống sẽ thực hiện N lần vòng DelayLoop.
Cách xác định DelayCount sẽ dựa vào giá trị xung đếm hệ thống (clocktick) tại địa chỉ 0040:006C (4 byte). Mỗi giây, nhờ kích hoạt của interrupt 08, giá trị tại địa chỉ này sẽ tăng lên 18.2 lần.
Trình biên dịch sẽ đặt vào DX:AX một giá trị cố định và gọi vòng lặp DelayLoop. Trong vòng lặp này, giá trị DX:AX sẽ được thay đổi cho tới khi clocktick của hệ thống thay đổi.
Đó là độ thay đổi của DX:AX trong 1 clocktick (1/18.2s). Để có giá trị này cho 1 mili giây, giá trị DX:AX sẽ được chia cho 55 (xin để ý: 18.2 * 55 = 1000ms=1second).
Đoạn lệnh gây lỗi trong chương trình chính là đoạn chia DX:AX cho CX. Về mặt nguyên tắc, thương số sẽ được chứa trong AX và số dư trong DX. Tuy nhiên, do DX:AX quá lớn, giá trị DX:AX chia cho CX sẽ vượt qua giá trị tối đa của 1 word (FFFFh) và hệ thống phát sinh thông báo lỗi.
Với máy Celeron 333 của tôi, giá trị nhận được khi chạy tới lệnh tại địa chỉ ****:008E là DX= 0045h, AX=4EEAh. Do đó, phép chia DX:AX (00454EEAh) cho CX(55=37h) sẽ làm tràn số và gây ra lỗi runtime. Nguyên nhân của vấn đề lỗi rõ ràng có liên quan tới tốc độ CPU. Do CPU chạy quá nhanh, độ biến đổi DX:AX là rất lớn và phép chia DX:AX cho CX bị tràn. CPU trong tầm 200Aạ266MHz có lẽ nằm ở ngưỡng của "vực thẳm" vì DX = 045h (giá trị ứng với Celeron 333MHz) * 266/333 = 37h = 55 (Phép chia DX:AX cho CX sẽ cho ra một giá trị xấp xỉ FFFFh. Do đó, với các hệ thống 200 - 266 MHz nhanh, lỗi trên có thể xuất hiện và với các hệ thống chậm, các bạn có thể chạy Turbo Pascal một cách yên ả). Như bạn có thể thấy, CPU tốc độ 300MHz, 333MHz hoặc cao hơn sẽ gây lỗi (do giá trị DX lớn hơn nhiều so với CX, dẫn tới DX:AX chia CX bị tràn). Như vậy, cách giải quyết của chúng ta sẽ xoay quanh việc làm giảm độ biến đổi DX:AX xuống. Điều này có thể thực hiện bằng cách làm chậm vòng DelayLoop xuống. Bạn có thể thêm 1 vài lệnh làm tiêu tốn CPU clock vào vòng lặp này như tôi thực hiện ở phần sau. Mọi thông báo sẽ chấm dứt và chương trình Pascal của bạn sẽ chạy một cách êm ả.
Cách giải quyết
Mọi vấn đề "đau đầu" nói trên sẽ được giải quyết bằng cách thay đổi nội dung tập tin CRT.TPU. Thông thường, khi bạn khởi động TURBO PASCAL (TP) hay BORLAND PASCAL (BP), CRT.TPU sẽ được hệ thống tự động nạp khi đọc TURBO.TPL (TPL=Turbo Pascal Library). Nhiệm vụ của chúng ta là thay đổi unit CRT trong TURBO.TPL.
Đơn giản nhất, bạn có thể chép tập tin TURBO.TPL từ tòa soạn về và ghi đè lên tập tin TURBO.TPL trong thư mục \BP\BIN (xem như \BP là thư mục Pascal của bạn). Tập tin TURBO.TPL mới chứa mọi thay đổi cần thiết giúp bạn có thể chạy BORLAND PASCAL với bộ xử lý Intel Pentium Celeron (và hy vọng mọi bộ xử lý khác "có vấn đề").
Nếu bạn không thể ghé tòa soạn và có thể truy xuất Internet hay e-mail, xin gửi e-mail cho ,, tôi sẽ gửi file TURBO.TPL lại cho bạn theo dạng attachment.
Và cuối cùng, nếu bạn không thể chép tập tin từ tòa soạn và cũng không có điều kiện sử dụng Internet, nếu cho mình là một lập trình viên Pro, hãy làm như sau:
Trước khi tiến hành các bước sau, hãy lưu lại tập tin TURBO.TPL trong \BP\BIN để đề phòng mọi bất trắc.
Bước 1: Sửa lại nội dung unit CRT
Với một bản cài đầy đủ của Borland Pascal 7.0, bạn sẽ có thư mục \BP\CRT, trong đó có 2 tập tin CRT.ASM và CRT.PAS. Đầu tiên, hãy thay đổi nội dung CRT.ASM như sau (hãy bấm Ctrl Q-L và tìm từ DelayLoop và phần mã sau):
; Delay one timer tick or by CX iterationsDelayLoop:
@@1:
SUB AX,1
SBB DX,0
JC @@2
CMP BL,ES:[DI]
JE @@1
@@2: RET
trở thành:
; Delay one timer tick or by CX iterations
DelayLoop:
@@1:
push ax ; bắt đầu các hàng chèn thêm
push cx push dx
mov ax,0
mov cx,1
mov dx,0
div cx ;với 10 lệnh div cx, DelayCount = 5F0h div cx; đủ cho bạn chạy với CPU có tốc độ < 8GHz
div cx ; về mặt lý thuyết.
div cx
div cx
div cx
div cx
div cx
div cx
div cx
pop dx
pop cx
pop ax ; kết thúc các hàng chèn thêm
SUB AX,1
SBB DX,0
JC @@2
CMP BL,ES:[DI]
JE @@1
@@2: RET
Như tôi có trình bày ở phần trên, các dòng chèn thêm không thực hiện bất cứ một lệnh gì mà chỉ đơn thuần là làm chậm vòng DelayLoop lại. Điều này không có nghĩa là đồng hồ hệ thống sẽ chậm lại mà chỉ đơn thuần làm chậm vòng DelayLoop và giảm số lần lặp xuống.
Lệnh nguyên thủy SUB và SBB của vòng lặp được giữ nguyên, các lệnh DIV (sau khi lưu nội dung thanh ghi AX, CX, DX bằng loạt lệnh PUSH) nhằm làm chậm hơn vòng lặp. Sở dĩ tôi chọn các lệnh này là vì DIV là lệnh tiêu tốn nhiều clock của CPU (lệnh mất nhiều thời gian thực hiện). Bạn có thể thêm số lần DIV nếu cảm thấy cần thiết. Cuối cùng, loạt lệnh POP hoàn trả lại nội dung các thanh ghi.
Bước 2. Chép tập tin SE.ASM từ thư mục \BP\SYS vào thư mục \BP\CRT. Tập tin SE.ASM chứa một số định nghĩa cho quá trình dịch các Unit của Borland (hay Turbo) Pascal.
Bước 3. Tiến hành dịch CRT.ASM bằng lệnh:
\BP\BIN\TASM CRT.ASM
Máy sẽ phát ra một vài cảnh báo về cách sử dụng tên biến. Cứ "nhắm mắt làm ngơ", chẳng có gì nghiêm trọng đâu.
Bước 4. Chạy Borland Pascal và tiến hành dịch (bấm F9) \BP\CRT\CRT.PAS để tạo ra CRT.TPU. Chép CRT.TPU vào thư mục \BP\BIN.
Bước 5. Chuyển thư mục hiện thời qua \BP\BIN
Xóa unit CRT trong TURBO.TPL bằng lệnh:
TPUMOVER TURBO.TPL -CRT
Sau đó, đưa unit CRT mới sửa vào TURBO.TPL
TPUMOVER TURBO.TPL +CRT
TURBO.TPL đã sẵn sàng. Hãy khởi động lại Borland Pascal. Hy vọng rằng mọi hàm của bạn đều được thực hiện một cách chính xác và Turbo Pascal sẽ chẳng còn phát ra những thông báo lỗi khó chịu "Run time error 200" khi bạn sử dụng unit CRT nữa.
Giải pháp cho các chương trình Pascal đã biên dịch nhưng không còn source
Chắc rằng trong số các chương trình bạn đã biên dịch bằng Borland Pascal 7, một số chương trình sử dụng unit CRT cũng sẽ gặp phải lỗi Division Error kể trên. Nếu các bạn còn giữ source, biên dịch lại chương trình với unit TURBO.TPL đã hiệu chỉnh không phải là "chuyện lớn". Thế nhưng phải làm thế nào nếu bạn không còn hay không có source các chương trình trên.
Để ý rằng do chương trình EXE đã được dịch, việc thay đổi vòng lặp DelayLoop để không ảnh hưởng tới các phần khác của chương trình là không thực hiện được. Do đó, chúng ta cần thay đổi trực tiếp đoạn mã lệnh gây lỗi.
Giải pháp tôi xin đề cử tới các bạn là thay đoạn lệnh:
****:008e: mov CX,55
****:0091: div CX
bằng một lệnh đơn giản: mov AX,0FFFFh vì FFFFh là giá trị tối đa mà DelayCount (biến dạng word 2 byte) có thể nhận được. Tuy nhiên, để không làm biến đổi địa chỉ các phần khác, bạn nhớ đặt thêm 1 lệnh 2 byte trước lệnh move này (do 2 lệnh nguyên thủy chiếm 5 byte bộ nhớ còn lệnh mov AX,0FFFFh chỉ chiếm 3 byte. Giải pháp này dẫn tới việc nếu bạn dùng hàm delay, máy tính sẽ chờ ít hơn (nhanh hơn) so với thời gian bạn yêu cầu, tuy nhiên trong đa số các trường hợp, điều này không dẫn tới hậu quả gì nghiêm trọng. Để tránh sự phiền hà khi tìm kiếm và thay đổi phần mã gây lỗi, tôi có kèm thêm chương trình PATCH.EXE (có thể chép tại Tòa Soạn PCW). Chương trình này sẽ tự động thực hiện việc thay đổi đoạn mã kể trên. Chỉ có đôi điều bạn cần lưu ý:
1. Chỉ dùng PATCH với các chương trình phát sinh lỗi khi chạy như kể trên.
2. Hãy lưu file nguyên thủy trước khi thực hiện thay đổi.
Chúc bạn thành công!
__________________
Edited: Không được chèn anh quá khổ vào chữ ký