Tuesday, April 24, 2012
Displaying special characters the hard way
Back in 1991 a programming language called Clipper (version Summer'87) was a popular choice for making simple business applications for IBM PC computers. Clipper was a specialised language designed to easily create business forms of 25 rows by 80 columns of characters. It was also very simple to read and write data using dBase IV format dbf files.
It had one tiny problem back then - it did not handle special Polish characters: ĄĆĘŁŃÓŚŹŻ, which at that time were encoded typically in the Mazovia code page.
Luckily, Clipper was extensible through the assembly language, so somebody wrote this little 8086 library to write and read characters using processor interrupts. I don't know who wrote it, because there was no author in the file, and every Clipper programmer had the source, so I assume it was, and is, in the public domain. The version presented here has been slightly updated by me, mainly by adding comments.
Please note: this program is very fragile and dangerous: inputs are not validated, which makes buffer overrun a possibility.
Clipper commands:
SCREENSTR read characters from SCREEN to Clipper STRing.
STRSCREEN write characters from Clipper STRing to SCREEN.
Assembly functions:
__RETNI, __PARNI, _RETC, __PARC allow passing of data to/from Clipper.
__PARNI, for example, allows passing of a Clipper numeric value to the assembly language, see Nantucket's Guide to Clipper here.
__PARC places the address of a Clipper character string in DX:AX.
16 bit registers like DX can can be accessed as DH and DL - higher and lower byte. Read the guide to 8086 registers, by Thomas Scarff, and the guide to Input and Output (I/O) in 8086 by Joe Carthy for more information.
And here is the assembly code:
PUBLIC SCREENSTR
PUBLIC STRSCREEN
EXTRN __RETNI:FAR
EXTRN __PARNI:FAR
EXTRN __RETC :FAR
EXTRN __PARC :FAR
DGROUP GROUP DATASG
DATASG SEGMENT 'DATA'
; string to return to Clipper
RETSTR DW 80 DUP (0)
DATASG ENDS
_PROG SEGMENT 'CODE'
; cs -> code, ds -> data
ASSUME cs:_PROG,ds:DGROUP
SCREENSTR PROC FAR
; save registers
push bp
mov bp,sp
push ds
push es
push si
push di
; get param. #1: row into dh
mov ax,1
push ax
call __PARNI
add sp,2
mov dh, al
push dx
; get param. #2: col into dl
mov ax,2
push ax
call __PARNI
add sp,2
pop dx
mov dl, al
; get video page in bh
mov ah,0fh
int 10h
; set cursor
mov ah,02h
int 10h
; get param. #3: count into cl
mov ax,3
push ax
call __PARNI
add sp,2
mov cl, al
mov ch, 0
; di is an index for RETSTR
mov di, 0
push di
; start loop1
; bh: video page, read char
loop1: mov ah,08h
int 10h
; write to RETSTR
pop di
mov RETSTR[di],ax
; inc. offset
inc di
inc di
push di
; inc column
inc dl
; set cursor
mov ah,02h
int 10h
; loop: dec cx
loop loop1
; restore registers
pop di
pop di
pop si
pop es
pop ds
pop bp
; return string
mov dx, seg RETSTR
mov ax, offset RETSTR
push dx
push ax
call __RETC
add sp,4
ret
SCREENSTR ENDP
STRSCREEN PROC FAR
push bp
mov bp,sp
push ds
push es
push si
push di
; get param. #2: row
mov ax,2
push ax
call __PARNI
add sp,2
mov dh, al
push dx
; get param. #3: col
mov ax,3
push ax
call __PARNI
add sp,2
pop dx
mov dl, al
push dx
; get param. #4: count
mov ax,4
push ax
call __PARNI
add sp,2
mov ch,0
mov cl, al
push cx
; get param. #1: string
mov ax,1
push ax
call __PARC
add sp,2
mov es,dx
mov bp,ax
; get video page (in bh)
mov ah,0Fh
int 10h
mov bl,0
; write to screen
mov ah,13h
mov al,2
pop cx
pop dx
int 10h
pop di
pop si
pop es
pop ds
pop bp
ret
STRSCREEN ENDP
_prog ENDS
END
No comments:
Post a Comment