Thảo luận thêm về Bộ hỗ trợ gõ tiếng Việt bằng ngôn ngữ lập trình Pascal
Trang 1Thảo luận thêm về Bộ hỗ trợ gõ tiếng Việt bằng ngôn ngữ lập trình Pascal
Nguyễn Hoành (nguyen_hoanh@hotmail.c
Trong bài viết 'Hỗ trợ gõ tiếng Việt bằngngôn ngữ lập trình Pascal' của tác giả Đỗ
Hồng Ngọc trên tạp chí Tinhọc & Nhà trường số tháng 12 năm 2001 có đề cập đến vấn
đề gõ tiếng Việtdưới môi trường DOS Trong bài viết này, tác giả đã nêu lên một cách có hệthống các thành phần mà một chương trình gõ tiếng Việt cần phải có Tuy nhiêntrong bài viết có một số chỗ chưa được chi tiết như chưa đề cập đến thủ tụcNewInt09, tải phông chữ lên bộ nhớ, làm cho độc giả chưa thể tạo được mộtchương trình gõ tiếng Việt hoàn chỉnh Tôi xin đề cập lại vấn đề này, và nhìnnhận dưới góc độ chi tiết hơn
Hoàn toàn đi theo hướng mà tác giả Đỗ Hồng Ngọc đãnêu, tôi xin nêu lên thư viện KeyVnTrs có chứa các mô đun tải phông chữ Việt,thay đổi ngắt cứng 09h Thư viện KeyVnTrs có các môđun chính như sau:
procedure KhoiTaoGoTelex -Khởi tạo và cài đặt phần gõ tiếng Việt
procedure LoaiBoGoTelex -Trả lại kiểu gõ thông thường cho hệ thống
functionTaiPhongTuTep(TenTep: string): boolean - Tải phông chữ việt (phông người sửdụng định nghĩa) từ tệp
procedure LoaiBoPhong - Loạibỏ tác dụng của phông chữ người sử dụng định nghĩa
Thư viện KeyVnTrs sử dụngbảng mã TCVN3 - Với bảng mã các chữ cần quan tâm như sau:
A= 65 E= 69 I= 73 O= 79 U= 85 Y= 89 Â=162 Ă=161 Ê=163 Ô=164 Ơ=165 Ư=166 a= 97 e=101 i=105 o=111 u=117 y=121 â=169 ă=168 ê=170 ô=171 ơ=172 ư=173 á=184 é=208 í=221 ó=227 ú=243 ý=253 ấ=202 ắ=190 ế=213 ố=232 ớ=237 ứ=248 à=181 è=204 ì=215 ò=223 ù=239 ỳ=250 ầ=199 ằ=187 ề=210 ồ=229 ờ=234 ừ=245 ả=182 ẻ=206 ỉ=216 ỏ=225 ủ=241 ỷ=251 ẩ=200 ẳ=188 ể=211 ổ=230 ở=235 ử=246 ã=183 ẽ=207 ĩ=220 õ=226 ũ=242 ỹ=252 ẫ=201 ẵ=189 ễ=212 ỗ=231 ỡ=236 ữ=247 ạ=185 ẹ=209 ị=222 ọ=228 ụ=244 ỵ=254 ậ=203 ă=198 ệ=214 ộ=233 ợ=238 ự=249
Trang 2Nội dung thư viện KeyVnTrs:
unit KeyVnTrs;
interface
procedure KhoiTaoGoTelex;
procedure LoaiBoGoTelex;
function TaiPhongTuTep(TenTep: string): boolean; procedure LoaiBoPhong;
implementation
uses crt, dos;
const
dKh = 0; {Khong co dau}
dSa = 1; {Sac (')}
dHu = 2; {Huyen (`)}
dHo = 3; {Hoi (?)}
dNg = 4; {Nga (~)}
dNa = 5; {Nang (.)}
TapKyTuNA: array[1 12 * 7] of Char =</p> ('A', 'E', 'I', 'O', 'U', 'Y', 'Â', 'Ă', 'Ê', 'Ô', 'Ơ', 'Ư', 'á, 'é, 'í, 'ó, 'ú, 'ý, 'ấ, 'ắ, 'ế, 'ố, 'ớ, 'ứ,
'á', 'é', 'í', 'ó', 'ú', 'ý', 'ấ', 'ắ', 'ế', 'ố', 'ớ', 'ứ',
'à', 'è', 'ì', 'ò', 'ù', 'ỳ', 'ầ', 'ằ', 'ề', 'ồ', 'ờ', 'ừ',
'ả', 'ẻ', 'ỉ', 'ỏ', 'ủ', 'ỷ', 'ẩ', 'ẳ', 'ể', 'ổ', 'ở', 'ử',
'ã', 'ẽ', 'ĩ', 'õ', 'ũ', 'ỹ', 'ẫ', 'ẵ', 'ễ', 'ỗ', 'ỡ', 'ữ',
Trang 3'ạ', 'ẹ', 'ị', 'ọ', 'ụ', 'ỵ', 'ậ', 'ặ', 'ệ', 'ộ', 'ợ', 'ự');
var
TuHt: string; {Từ hiện tại đang gõ}
Int09Old: procedure; {Địa chỉ bộ nhớ của ngắt 09h cũ} FontArr: array[0 255, 0 15] of byte;
procedure ThemHDP(c:string);
{Thêm một chuỗi vào hàng đợibàn phím}
var i: Byte;
procedure DatChuVaoHDP(kt: Char);
{Đặt một chữ vào hàng đợi bàn phím}
var< > r: registers;
begin
Case kt of
#8: if TuHt <> '' then Delete(TuHt, Length(TuHt), 1); #0 #31: TuHt:= '';
#32 #255: TuHt:= TuHt + kt;
end;
if Length(TuHt) > 200 then delete(TuHt, 1, 50); r.ah:= 05;
r.cl:= ord(kt);
r.ch:= 0;
intr($16, r);
end;
Trang 4for i:= 1 to length(c) do begin
DatChuVaoHDP(c[i]);
end;
end;
procedure ThemHDPMR(PMR:Char);
{Thêm một phím mở rộng vàohàng đợi bàn phím} var r: registers;
begin
TuHt:= '';
r.ah:= 05;
r.cl:= 0;
r.ch:= ord(PMR);
intr($16, r);
end;
function ViTriNguyenAm(c:Char): Byte;
{Tìm chỉ số của nguyên âm ctrong mảng}
var i: Byte;
begin
for i:= 1 to 12 * 7 do begin
if c = TapKyTuNA[i] then begin
ViTriNguyenAm:= i;
Exit;
Trang 5end;
end;
VitriNguyenAm:= 0;
end;
function LaNguyenAm(c:Char): boolean;
{Có đúng ký tự c là nguyênâm không}
begin
LaNguyenAm:= (ViTriNguyenAm(c) <> 0);
end;
function BoDauChu(c: Char;DauBo: Byte): Char;
{Bỏ dấu vào chữ}
var Vt, ChuGoc, DauHt: integer;
begin
Vt:= ViTriNguyenAm(c);
ChuGoc:= (Vt mod 12); if ChuGoc = 0 then ChuGoc:= 12;
DauHt:= ((Vt - 1) div 12) - 1; if DauHt = -1 then DauHt:= 0;
if DauHt <> DauBo then BoDauChu:= TapKyTuNA[(DauBo + 1) *12 + ChuGoc] else BoDauChu:= c;
end;
function LayDau(CumTu:string): Byte;
{Xét xem dấu được bỏ ở đâutrong các nguyên âm trong cụm từ}
const
DauSau: array[1 12] of string[2] =</p>
Trang 6('iế, 'oá, 'oấ, 'oắ, 'oé, 'uý, 'uấ, 'uế, 'uố, 'uớ,'yế', 'ướ); var i: Byte;
begin
if (CumTu[1] in ['A' 'Z']) then Inc(CumTu[1], 32);
if (CumTu[2] in ['A' 'Z']) then Inc(CumTu[2], 32); for i:= 1 to 12 do
if CumTu = DauSau[i] then begin
LayDau:= 1;
Exit;
end;
LayDau:= 0;
end;
function ViTriBoDau: Byte;
{Xác định vị trí bỏ dấu}
var l, i: Byte;
begin
l:= Length(TuHt);
for i:= L downto 1 do begin
if TuHt[i]=' ' then begin
ViTriBoDau:= 0;
Exit;
end;
if LaNguyenAm(TuHt[i]) then begin
Trang 7if (i>1) and (LaNguyenAm(TuHt[i-1])) then begin
ViTriBoDau:= i - 1 + LayDau(Copy(TuHt, i-1, 2));
if (i>2) and (TuHt[i-2] = 'g') and (TuHt[i-1] = 'í)then ViTriBoDau:= i; end
else ViTriBoDau:= i;
Exit;
end;
end;
ViTriBoDau:= 0;
end;
function CoNguyenAm:boolean;
{Có nguyên âm trong từ cuốicùng khi gõ không}
begin
CoNguyenAm:= (ViTriBoDau <> 0)
end;
<>procedure BoDau(dau: Byte);
{Bỏ dấu vào từ cuối cùng}
var i, DoDaiXoa, Vt: Byte;
NgAm: Char;
Temp: string;
begin
Vt:= ViTriBoDau; {Tim vi tri can bo dau}
NgAm:= TuHt[Vt]; {Lay nguyen am tai vi tri do}
Trang 8NgAm:= BoDauChu(NgAm, dau); {Them dau moi cho nguyen am}
DoDaiXoa:= Length(TuHt) - Vt + 1;
Temp:= '';
for i:= 1 to DoDaiXoa do Temp:= Temp + #8;
Temp:= Temp + NgAm;
for i:= Vt + 1 to Length(TuHt) do Temp:= Temp + TuHt[i];
ThemHDP(Temp);
end;
procedure XulyPhim(ch:Char);
{Xử lý khi có một phím gõvào}
var KQ, ChuCuoi: Char;
begin
ChuCuoi:= TuHt[Length(TuHt)];
case ch of
'D': if Upcase(ChuCuoi) = 'D' then ThemHDP(#8 + 'Đ') elseThemHDP('D'); 'A': if ChuCuoi = 'A' then ThemHDP(#8 + 'Â') else ThemHDP('A');
'E': if ChuCuoi = 'E' then ThemHDP(#8 + 'Ê') else ThemHDP('E');
'O': if ChuCuoi = 'O' then ThemHDP(#8 + 'Ô') else ThemHDP('O');
'd': if Upcase(ChuCuoi) = 'D' then ThemHDP(#8 + 'đ') elseThemHDP('d'); 'á: if ChuCuoi = 'á then ThemHDP(#8 + 'ấ) else ThemHDP('á);
'é: if ChuCuoi = 'é then ThemHDP(#8 + 'ế) else ThemHDP('é);
'ó: if ChuCuoi = 'ó then ThemHDP(#8 + 'ố) else ThemHDP('ó);
'W': case Upcase(ChuCuoi) of
Trang 9'A': ThemHDP(#8 + 'Ă');
'O': ThemHDP(#8 + 'Ơ');
'U': ThemHDP(#8 + 'Ư');
'Ư': ThemHDP(#8 + 'W');
else ThemHDP('Ư');
end;
'w': case ChuCuoi of
'á: ThemHDP(#8 + 'ắ);
'ó: ThemHDP(#8 + 'ớ);
'ú: ThemHDP(#8 + 'ứ);
'ứ: ThemHDP(#8 + 'w');
else ThemHDP('ứ);
end;
's': if CoNguyenAm then BoDau(dSa) else ThemHDP('s'); 'f': if CoNguyenAm then BoDau(dHu) else ThemHDP('f'); 'r': if CoNguyenAm then BoDau(dHo) else ThemHDP('r'); 'x': if CoNguyenAm thenBoDau(dNg) else ThemHDP('x'); 'j': if CoNguyenAm then BoDau(dNa) else ThemHDP('j'); 'z': if CoNguyenAm then BoDau(dKh) else ThemHDP('z'); else ThemHDP(Ch);
end;
end;
procedure NewInt09;interrupt;
Trang 10{Ngắt 09h mới - bắt buộcphải có từ khoá interrupt}
var
Temp: array[1 16] of Char;
SoPhim, i: Byte;
begin
inline($9C);{PUSHF - phải có trước khi gọi một ngắt cứng khác} Int09Old;< > {Gọi ngắt 09cũ}
SoPhim:= 0;
while keypressed do begin
Inc(SoPhim);
Temp[SoPhim]:= ReadKey;
end;
i:= 0;
while i < Sophim do begin
Inc(i);
if Temp[i]=#0 then begin
ThemHDPMR(temp[i+1]);
Inc(i);
end
else begin
Xulyphim(Temp[i]);
end;
end;
Trang 11procedure KhoiTaoGoTelex;
begin
TuHt:='';
GetIntVec($09,@Int09Old);
SetIntVec($09,Ađr(NewInt09));
end;
procedure LoaiBoGoTelex;
begin
SetIntVec($09,@Int09Old);
end;
functionTaiPhongTuTep(TenTep: string): boolean; var f: file;< > r: registers;< > num: integer;
begin
Assign(f, TenTep);
{$i-} {Từ khoá này thêm vào để không bắt lỗi vào ra tệp} Reset(f,1);
BlockRead(f, FontArr, SizeOf(FontArr), num);
Close(f);
{$i+}
if (ioresult<>0) or (num <> SizeOf(FontArr)) thenbegin TaiPhongTuTep:= false;
Exit;
Trang 12r.es:= Seg(FontArr); {Địa chỉ khối phông chữ cần nạp}
r.bp:= Ofs(FontArr);
r.ax:= $1110; {Tải phông chữ người sử dụng}
r.bx:= $1000; {bh = $10 = số byte / ký tự}
r.cx:= $0100; {$0100 = 256 = Số ký tự cần nạp}
r.dx:= $0000; {Bắt đầu từ ký tự #0}
intr($10, r);
TaiPhongTuTep:= True;
end;
procedure LoaiBoPhong;
var
r: registers;
begin
r.ax:= $1114; {Tải phông 8x16 của ROM}
r.bl:= 0;
intr($10, r);
end;
end
Trong môđun TaiPhongTuTep cần phải có một tệp chứaphông 8x16, tệp này có độ dài bằng 4096 Byte (8 bit * 16 dòng * 256 ký tự) Nếukhông có tệp này hoặc độ dài tệp này khác 4096 byte thì môđun sẽ bỏ qua quátrình tải phông
Và ở chương trình chính, để sử dụng thư việnKeyVnTrs:
uses KeyVnTrs, Crt;
Trang 13var s: string;
begin
Clrscr;
KhoiTaoGoTelex;
TaiPhongTuTep('Font.dat');
WriteLn('Hãy nhập vào một chuỗi: ');
ReadLn(s);
LoaiBoGoTelex;
LoaiBoPhong;
end
Nếu muốn tạo một chương trình thường trú để gõ, bạnchỉ cần một đoạn code ngắn:
{$M 800,0,0}
uses KeyVnTrs, Dos;
begin
KhoiTaoGoTelex;
TaiPhongTuTep('Font.dat');
WriteLn;
WriteLn('Chương trình bàn phìm tiếng Việt dưới DOS - (c) NguyễnHoành 1997')
Keep(0);
end
Nhớ là bạn phải có khoá $M để giới hạn bộ nhớ, nếukhông máy sẽ bị treo Để gỡ bỏ
chương trình thường trú khỏi bộ nhớ, bạn nêntham khảo thêm cuốn 'Mẹo và thủ thuật lập trình bằng Turbo Pascal 5.5'của nhà xuất bản Khoa Học và Kỹ Thuật.
Nguyễn Hoành
Trang 14Email:nguyen_hoanh@hotmail.com