; ; 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, 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, 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, ; Ugg, I just found a hole in the ; macros I've written. This really ; should be call_c cputs,. ret clreol ENDP window PROC C uses ds, left:word, top:word, right:word, bottom:word .data win_left dw 1 win_top dw 1 win_right dw 80 win_bottom dw 24 .code mov ax, @data mov ds,ax ABS_VALIDATE_COLUMN left, done ABS_VALIDATE_ROW top, done ABS_VALIDATE_COLUMN right, done ABS_VALIDATE_ROW bottom, done mov ax,left mov win_left,ax mov ax,top mov win_top,ax mov ax,right mov win_right,ax mov ax,bottom mov win_bottom,ax done: ret window 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, .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, ; see above. should be 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