;
; RBCONIO.ASM
;
; Turbo C++ 1.0 conio.h routines for the Rainbow.
; 1992-03-06 by M. Warner Losh
; Donated to the Public Domain. No claim of Copyright made by the
; author.
;
;
; gotoxy is derived, in part, from the EWF.ASM program.
;
; The call_c macro is pure sleeze, or a work of art, depending on
; who you ask. I'd love to know how other people deal with calling
; functions written in 'C' or with a 'C' interface from MASM.
;
; WARING: Yes, you don't get off quite that easily. These routines are
; the bare essentials that I needed to get moria working on my Rainbow
; at reasonable speeds. I pay, at best, lip service to the window()
; call. There are a couple of hooks in the code to deal with windows,
; but they are just hooks, with no flesh hanging from them. There is
; no translation of 8 bit characters from the IBM world to the Rainbow
; world, since I'm basically lazy and the only thing it effects is the
; map the entire level function. Since that is real high on the list
; of command that I rarely use, I haven't put the effort into making it
; work.
;
; The gettext() and puttext() calls are probably not compatable with
; thier PC conterparts in terms of the data stored in the buffers.
; However I could find no good definition of what they should contain,
; so I'm not too worried.
;
.MODEL LARGE,C
.CODE
;
; push_args A helper macro for the call_c and call_pascal
; functions.
;
push_args macro args
irp x,<&args> ;; Foreach of the args that were passed.
push x ;; Push the arg.
endm
endm
;
; call_c A macro that makes it painless to call 'C' functions
; with parameters from assembler.
;
call_c MACRO who,args
nargs = 0 ;; We have no args to start with
IFNB <&args> ;; If we have args
IRP x,<&args> ;; For each of the args that we have
IF NARGS EQ 0 ;; For the first one we set newargs
newargs equ <&x> ;; equal to the arg
ELSE ;; otherwise we prepend this arg to
newargs catstr <&x,>,newargs ;; the string we're building
ENDIF
nargs = nargs + 1 ;; bump total count
ENDM
push_args %newargs ;; push the args.
ENDIF
call &who ;; make the call
IFNB <&args> ;; If we have args,
ADD SP,nargs*2 ;; fix the stack
ENDIF ;; That's all folks...
ENDM
RB_ATTR_OFFSET EQU 1000H ; offset of char and attributes
RB_LINE_TABLE EQU 0EF4H ; Table of line pointers
RB_CUR_COLUMN EQU 0F41H ; offset of cursor column pos
RB_CUR_LINE EQU 0F42H ; offset of cursor line pos
RB_MAX_ROW EQU 24 ; Biggest row
RB_MAX_COLUMN EQU 80 ; Biggest column
RB_ROM EQU 18H ; Interrupt to use.
RB_PC_MAX_ROW EQU 25 ; Biggest row
RB_VIDEO_SEGMENT EQU 0EE00H ; Video offset.
_rb_cursor_off PROC Near
mov DI, 08H ; Cursor off function
INT RB_ROM
ret
_rb_cursor_off ENDP
_rb_cursor_on PROC Near
mov DI, 0AH ; Cursor on function
INT RB_ROM
ret
_rb_cursor_on ENDP
ABS_VALIDATE_COLUMN MACRO col, lbl
LOCAL lab1,lab2
mov ax, col ; get the column
or ax,ax ; is it 0?
jnz lab1 ; yes -> bale
mov ax,1
jmp lbl ; exit
lab1:
cmp ax, RB_MAX_COLUMN ; validate
jbe lab2 ; too big, bail
mov ax,1
jmp lbl ; exit
lab2:
ENDM
ABS_VALIDATE_ROW MACRO row, lbl
LOCAL lab1,lab2,lab3
mov ax, row ; get the column
or ax,ax ; is it 0?
jnz lab1 ; yes -> bale
mov ax,1
jmp lbl ; exit
lab1:
cmp ax, RB_MAX_ROW ; validate
jbe lab2 ; too big, bail
cmp ax, RB_PC_MAX_ROW ; Special case for PC's 25th line
je lab3
mov ax,1
jmp lbl ; exit
lab3:
dec ax ; 25->24
mov row, ax ; save it
lab2:
ENDM
XLATE_COORDS MACRO col, row, lbl
ABS_VALIDATE_ROW row, lbl
ABS_VALIDATE_COLUMN col, lbl
ENDM
;
; void absgotoxy(int x,int y)
; Move the cursor to x, y (x == column, y == row)
;
gotoxy PROC C uses ES DS SI DI, locx:word, locy:word
XLATE_COORDS locx, locy, punt
call _rb_cursor_off ; Turn off the cursor.
mov ax, RB_VIDEO_SEGMENT
mov ds, ax ; DS: points at the video segment.
mov ax, locy ; Row
mov DS:BYTE PTR RB_CUR_LINE, AL ; set the row
mov ax, locx ; Column into AX
mov DS:BYTE PTR RB_CUR_COLUMN, AL ; set the column
call _rb_cursor_on ; Turn off the cursor.
punt:
ret
gotoxy ENDP
;
; int putch(int c)
; Puts c to console. Returns c.
;
putch PROC C USES es ds si di, c:word
mov ax,c ; Character to put
xor di,di ; 0 == PUTCH
int RB_ROM ; do it
mov ax,c ; compatability return value
ret ; done
putch ENDP
;
; int cputs(char *str)
;
cputs PROC C USES es ds si di, str:dword
LOCAL retval:word
; DB 0CCH ; INT 3
cld
lds si, str
cputs1:
lodsb
or al,al
jz done
; push ax
; call putch
; add sp,2
call_c putch,<ax>
mov retval,ax
jmp cputs1
done:
mov ax,retval
ret
cputs ENDP
;
; int getch()
; gets a character from the colsole.
;
getch PROC C USES es ds si di
getch1:
mov di,2 ; getch
int RB_ROM ; do it
or cl,cl ; CL == 0 when no char yet.
jz getch1 ; loop if cl == 0
xor ah,ah ; fix return value
ret ; done
getch ENDP
;
; int kbhit()
; returns 0 if no char, else non-zero (0xff in our case)
;
kbhit PROC C USES es ds si di
mov di,4 ; Console status
int RB_ROM ; do it
xor ax,ax ; Make sure ah has no trash in it
mov al,cl ; cl has status
ret
kbhit ENDP
;
; int gettext(int left, int top, int right, int bottom, void *bfr)
; returns 0 if OK, 1 if not
; The manual doesn't describe the format of bfr, so I'm
; storing the bytes in a stripes. That is I store each
; row's chars, then its attributes. Should make puttext
; easier to write as well.
;
gettext PROC C uses es ds si di, left:word, top:word, right:word,\
bottom:word, bfr:dword
call _rb_cursor_off
mov ax, RB_VIDEO_SEGMENT
mov ds, ax ; DS is video segment now
; make sure the coords are OK
ABS_VALIDATE_COLUMN left, done
ABS_VALIDATE_ROW top, done
ABS_VALIDATE_COLUMN right, done
ABS_VALIDATE_ROW bottom, done
; OK, they are good, or we wouldn't be here
; bx == current row number
; cx == number of columns to xfer
mov bx, top
mov ax,left ; Adjust left to make life
dec ax ; easier later.
mov left,ax
les di, bfr ; where to store the data
mov dx, right ; cx = right - left + 1
sub dx, left
cld ; Make sure we go forward..
outer_loop:
mov si,bx ; do the indexing
dec si ; 0, not 1, relative
shl si,1 ; words, not bytes
mov ax, RB_LINE_TABLE[si] ; put the two together
mov si,ax
add si, left ; offset
mov cx,dx ; Move in the count
push si ; Save the source
rep movsb ; Move this row's characters
pop si ; restore the source
add si, RB_ATTR_OFFSET ; Make it point to the attributes
mov cx,dx ; move in the count
rep movsb ; Move the attributes in
inc bx ; bump the row number
mov ax, bottom ; load in the limit
cmp bx,ax
jbe outer_loop ; loop if we aren't there yet.
xor AX,AX ; Yup, life is good
done:
call _rb_cursor_on
ret
gettext ENDP
;
; void put_put_line(type, row, col, count, seg, chars, attr)
; type type of xfer. 0 both, 1 attr, 2 chars
; row row to put this into
; col col to put this into
; count number of chars to stuff
; seg segment of characters
; chars offset within seg of chars to use
; attr offset within seg of attributes to use
;
; NB chars and attr must be relative to the same segment.
;
_rb_put_line PROC C USES es ds di si, xfer_type:word, row:word,\
col:word, count:word, data_seg:word, chars:word,\
attr:word
mov ax,row
mov bl,al
mov ax,col
mov bh,al
mov cx,count
mov dx,attr ; Please read the manual. It tells you
mov si,chars ; that attr is in dx and chars in si.
mov ax,xfer_type
mov bp,data_seg ; MUST BE DONE LAST, since bp appears
; in the above, you just can't see it
mov di, 14H ; Now then, don't we need this, lest
; the INT 18H become effectively a nop?
INT 18H
ret
_rb_put_line ENDP
;
; puttext(left, top, right, bottom, bfr)
;
puttext PROC C USES es ds di si, left:word, top:word, right:word,\
bottom:word, bfr:dword
LOCAL wid,row
;
; wid == width of the row
; row == current row
;
; make sure the coords are OK
call _rb_cursor_off
ABS_VALIDATE_COLUMN left, done
ABS_VALIDATE_ROW top, done
ABS_VALIDATE_COLUMN right, done
ABS_VALIDATE_ROW bottom, done
mov ax, right ; ax = right - left + 1
sub ax, left
inc ax
mov wid,ax ; save the width
les di,bfr ; es:di == buffer.
mov ax,top ; row = top
outer_loop:
mov row,ax ; Inside loop so end of loop stores OK.
mov si,di
add si,wid ; move poitner to next set of chars
xor ax,ax ; attributes and characters
; push si ; attributes
; push di ; chars
; push es ; segment
; push wid ; width
; push left ; col
; push row ; row
; push ax ; Push type == BOTH.
; call _rb_put_line ; do it
; add sp,14 ; clean that stack!
call_c _rb_put_line,<ax,row,left,wid,es,di,si>
add di,wid ; move poitner to attributes
add di,wid ; move poitner to attributes
mov ax,row
inc ax
cmp ax,bottom
jbe outer_loop ; note this includes bottom.
done:
call _rb_cursor_on
ret
puttext ENDP
clreol PROC C uses ds
.data
clreol_str db 27,'[K',0
.code
mov ax, @data
mov ds, ax
mov ax, offset byte ptr clreol_str
; push ds
; push ax
; call cputs
; add sp,4
call_c cputs,<ax,ds> ; Ugg, I just found a hole in the
; macros I've written. This really
; should be call_c cputs,<ds:ax>.
ret
clreol ENDP
clrscr PROC C Uses DS
mov ax, @data
mov ds, ax
; push win_top
; push win_left
; call gotoxy
; add sp,4
call_c gotoxy,<win_left,win_top>
.data
clrscr_str db 27,'[J',0
.code
mov ax, offset byte ptr clrscr_str
; push ds
; push ax
; call cputs
; add sp,4
call_c cputs,<ax,ds> ; see above. should be <ds:ax>
ret
clrscr ENDP
wherex PROC C uses ds
mov ax, RB_VIDEO_SEGMENT
mov ds, ax ; DS: points at the video segment.
xor ax, ax
mov al, DS:[RB_CUR_COLUMN]
ret
wherex ENDP
wherey PROC C
mov ax, RB_VIDEO_SEGMENT
mov ds, ax ; DS: points at the video segment.
xor ax, ax
mov al, DS:[RB_CUR_LINE]
ret
wherey ENDP
END