5

I'm using TASM 1.4 and I'm trying to make an output that will display different colored sentences, all in the same screen. I can make something that displays colored texts, but the words all have the same color. How do I make something that displays different colored strings/sentences? For example, something like:

Hi, what group would you like to join?
The Founders
The Vox Populi

With "Hi, what group would you like to join?" colored green. "The Founders" colored blue. And "The Vox Populi" colored red, and maybe I'd want another sentence that blinks? I was only able to display them all blue or red or green. Any help? Thanks.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
xTan
  • 77
  • 1
  • 11
  • 1
    possible duplicate of [TASM 1.4 - Changing background color without clearing the screen?](http://stackoverflow.com/questions/32619237/tasm-1-4-changing-background-color-without-clearing-the-screen) – Ross Ridge Sep 17 '15 at 15:20
  • @RossRidge hi! um, no this is actually a different issue. there, I was trying to change the background upon input without clearing previous texts but here, I'm just trying to display different colored texts on the same screen. – xTan Sep 17 '15 at 15:57
  • 1
    If my fuzzy memory is correct, DOS interrupts aren't very good at this. Bios interrupt 10h, subfunction 13h may be what you want. Will even do "rainbow" text with each character a different color. You know Ralf Brown? – Frank Kotler Sep 17 '15 at 17:51
  • @FrankKotler : DOS interrupts weren't bad at it if you had a console driver like ANSI.SYS installed. Then you could use `int 21h` output routines and specify escape codes to change attributes, move the cursor, clear the screen. Unfortunately that mechanism was less efficient than bypassing the BIOS and/or DOS(int 21h) console driver and writing the attributes right to the video memory region starring @ 0xb800:0 . @xTan's previous question is quite similar, and I did suggest in the comments ways to tackle this issue. – Michael Petch Sep 17 '15 at 21:10
  • One issue is that @xTan appears to be new to assembly programming, and I did suggest creating a function/procedure to read the cursor location ; write each character in a string out byte by byte starting at that location with the specified attribute; keep track of where the cursor would be; update the cursor at the end. Personally I'd avoid the interrupt calls for updating the characters and attributes and write directly to the video memory, but for new developers the DOS interrupts and the BIOS may be easier to work with. – Michael Petch Sep 17 '15 at 21:20
  • What is the target for the program? MSDOS? Windows? Linux? – wallyk Sep 18 '15 at 00:49
  • @wallyk : If you review the other question the OP [asked previously](http://stackoverflow.com/questions/32619237/tasm-1-4-changing-background-color-without-clearing-the-screen). He is using TASM (which creates 16 bit programs). His code suggests he is targeting DOS (int 21h). TASM 1.4 Installer which he is apparently using is a DOSBox based environment. – Michael Petch Sep 18 '15 at 01:00
  • @MichaelPetch: In the 1980s, I used tasm 1.5 to create Windows programs. It is a whole different experience specifying color text on a MSwindow than text on a MSDOS screen. One learns to be very efficient and terse. – wallyk Sep 18 '15 at 02:07
  • @wallyk I should have made clear TASM 1.4 Installer is actually TASM3.0 and Turbo Debugger 3.1. I'm also old school. I bought Turbo-C in the mid 80s and got TASM 1.0 with Turbo-C 2.0. Under older versions of windows (Win2.x, Win 3.x, Win95x, Win98x) writing colored text to a DOS prompt was pretty much the same as it was in MS-DOS. Creating a WIn32 GUI application is much different when displaying colored text. ANSI.SYS driver was the old school way of allowing `int 21` to modify screen attributes through escape sequences, although less efficient than writing to video memory directly. – Michael Petch Sep 18 '15 at 02:15
  • @MichaelPetch: Yep, I developed some products for MSDOS and prototyped with `int 21` through ANSI.SYS (which was rather buggy in 1985) then `int 13` then eventually replaced it all with screen buffer writing. Also wrote several serial protocol drivers with interrupt driven i/o. It was really difficult to believe MSDOS and then Windows did not support that. – wallyk Sep 18 '15 at 03:24
  • 1
    @wallyk : I did video driver and serial port work as well back then (ran a BBS at the time). I don't recall ANSI.SYS being buggy, but it was dog slow with a lot of updates. That was because it was written as a layer on top of `int 10h` rather than doing direct writes to video memory. I remember the NANSI.SYS replacement that came out was a vast improvement since it bypassed `int 10h` video services. – Michael Petch Sep 18 '15 at 03:59

2 Answers2

5

You can use INT 10h / AH=13h for that purpose:

.MODEL small
.STACK 1000h

.DATA
    msg1 db "Hi, what group would you like to join?", 13, 10
    msg1_len = $ - msg1
    msg2 db "The Founders", 13, 10
    msg2_len = $ - msg2
    msg3 db "The Vox Populi", 13, 10
    msg3_len = $ - msg3
    msg4 db "Blinkers", 13, 10
    msg4_len = $ - msg4

.CODE

main PROC
    mov ax, @data
    mov ds, ax              ; Don't forget to initialize DS!
    mov es, ax              ; Segment needed for Int 10h/AH=13h

    lea bp, msg1            ; ES:BP = Far pointer to string
    mov cx, msg1_len        ; CX = Length of string
    mov bl, 2               ; green (http://stackoverflow.com/q/12556973/3512216)
    call print

    lea bp, msg2            ; ES:BP = Far pointer to string
    mov cx, msg2_len        ; CX = Length of string
    mov bl, 3               ; blue (http://stackoverflow.com/q/12556973/3512216)
    call print

    lea bp, msg3            ; ES:BP = Far pointer to string
    mov cx, msg3_len        ; CX = Length of string
    mov bl, 4               ; red (http://stackoverflow.com/q/12556973/3512216)
    call print

    lea bp, msg4            ; ES:BP = Far pointer to string
    mov cx, msg4_len        ; CX = Length of string
    mov bl, 8Eh             ; blinking yellow (Bit7 set, works at least in DOSBox)
    call print

    mov ax, 4C00h           ; Return 0
    int 21h
main ENDP

print PROC                  ; Arguments:
                            ;   ES:BP   Pointer to string
                            ;   CX      Length of string
                            ;   BL      Attribute (color)

    ; http://www.ctyme.com/intr/rb-0088.htm
    push cx                 ; Save CX (needed for Int 10h/AH=13h below)
    mov ah, 03h             ; VIDEO - GET CURSOR POSITION AND SIZE
    xor bh, bh              ; Page 0
    int 10h                 ; Call Video-BIOS => DX is current cursor position
    pop cx                  ; Restore CX

    ; http://www.ctyme.com/intr/rb-0210.htm
    mov ah, 13h             ; VIDEO - WRITE STRING (AT and later,EGA)
    mov al, 1               ; Mode 1: move cursor
    xor bh, bh              ; Page 0
    int 10h                 ; Call Video-BIOS

    ret
print ENDP

END main
rkhb
  • 14,159
  • 7
  • 32
  • 60
  • 1
    Just a side note, one place blinking won't work is in a 32-bit Windows DOS prompt – Michael Petch Sep 18 '15 at 22:50
  • @MichaelPetch: The second one is emu8086. But the question deals about "TASM 1.4", that is DOSBox - as you mentioned and I checked. – rkhb Sep 18 '15 at 22:53
  • The reason I mentioned it is because like I did you commented about blinking `blinking yellow (Bit7 set, works at least in DOSBox`. Some may wonder what environments you and I are alluding to that may not support it. As I said it was a side note, and wouldn't have said anything had I not seen your inline comment ;-) . And for what it is worth blinking seems to be supported in Bochs. I did up-vote your answer since it is another way to skin this cat. – Michael Petch Sep 18 '15 at 23:22
  • An observation with this code is that it won't work on an original 8086/8088 IBM-PC. `int 10h/ah=13h` wasn't available until the IBM/PC AT(80286). The code does have a note buried in it `(AT and later,EGA)` however it might be useful to mention this in the body of the answer, as it may be an important consideration that may go unnoticed unless someone carefully examines the code. – Michael Petch Sep 24 '15 at 20:01
  • Of course this will work in DOSBox since it emulates a 286+. Won't work in Tiny8086(8088 emulator). This type of answer may get copy and pasted to another environment by someone searching for a similar type of answer. I like your answer, just looking out for the next fellow that stumbles on this question/answer that may not be using DOSBox. – Michael Petch Sep 24 '15 at 20:08
4

I'm going to offer one way of achieving this with DOS int 21h.

The original poster says they are using TASM 1.4(TASM installer), which is a DOSBox based environment with TASM 3.0 and Turbo Debugger 3.1. In the days of DOS there was a video console driver that could be added to CONFIG.SYS with a command like DEVICE=C:\DOS\ANSI.SYS . This driver enhanced DOS int 21h output so you could specify attributes(background color, foreground color, blinking); clear the screen; reset back to default (usually black on white). DOSBox has a partial implementation of ANSI.SYS built in. The following code takes advantage of these ANSI codes:

.model small
.stack 100h

.data
a_cls        db 27, "[2J$"    ; Clear entire screen in currently set attributes
a_reset      db 27, "[0m$"    ; Reset attributes to standard (black on white)
a_blink      db 27, "[5m$"    ; Characters blink (blink doesn't work in all environments)
a_bright     db 27, "[1m$"    ; Bright colored characters
a_dim        db 27, "[2m$"    ; Dim colored characters
a_fg_black   db 27, "[30m$"   ; Foreground colors
a_fg_red     db 27, "[31m$"
a_fg_green   db 27, "[32m$"
a_fg_yellow  db 27, "[33m$"
a_fg_blue    db 27, "[34m$"
a_fg_magenta db 27, "[35m$"
a_fg_cyan    db 27, "[36m$"
a_fg_white   db 27, "[37m$"
a_bg_black   db 27, "[40m$"   ; Background colors
a_bg_red     db 27, "[41m$"
a_bg_green   db 27, "[42m$"
a_bg_yellow  db 27, "[43m$"
a_bg_blue    db 27, "[44m$"
a_bg_magenta db 27, "[45m$"
a_bg_cyan    db 27, "[46m$"
a_bg_white   db 27, "[47m$"

text1        db "Blinking Bright Yellow on Blue$"
text2        db " Back to Normal$" 
text3        db " Bright Yellow on Black$"
combined     db 27, "[0;5;2;31;42m Blinking Dim Red on Green$" 

.code
begin:  
    mov ax,@data
    mov ds,ax
    mov es,ax
    mov ah,09h
    lea dx,a_cls      ; clear screen
    int 21h           
    lea dx,a_bright   ; Bright colored characters
    int 21h
    lea dx,a_blink    ; make the characters blink (if supported)
    int 21h
    lea dx,a_fg_yellow; make the characters yellow
    int 21h
    lea dx,a_bg_blue  ; blue background for characters
    int 21h
    lea dx,text1      ; print text
    int 21h
    lea dx,a_reset    ; reset to defaults 
    int 21h
    lea dx,text2      ; print normal text 
    int 21h
    lea dx,a_bright   ; bright characters
    int 21h
    lea dx,a_fg_yellow; make the characters yellow
    int 21h
    lea dx,text3      ; print text
    int 21h
    lea dx,combined   ; print combined text
    int 21h
    lea dx,a_reset    ; reset to defaults before exiting back to DOS prompt
    int 21h
    .exit
end begin

In DOS, ANSI codes start with the ESCAPE character (decimal 27) followed by [ (left bracket). For attributes the m command can be used. Attributes can be separated by semi-colons. There are some other commands besides m and they can be found in the ANSI specs. If you review the code above you'll see where I have broken out the ANSI codes for a number of commands that may be of interest to the original poster.

The following code is an example of combining a number of attributes (using ANSI) rather than doing them separately.

combined     db 27, "[0;5;2;31;42mBlinking Dim Red on Green$"

Broken out we start with ESCAPE [ . 0 resets the current attributes to default (most hardware is white on black). The rest of the attributes are separated by SEMI COLONS. The next attribute 5 is blinking, the next is 2 which is dimmed color, 31 is foreground color red, 42 is green background followed by the command m . m ends the command and the characters after are the text to print.

Based on the escape sequences in the previous code, the following would fulfill the specific question the poster asked:

.model small
.stack 100h

.data
a_reset      db 27, "[0m$"    ; Reset attributes to standard (black on white)

             ; 13, 10 is carriage return line feed combination(CR/LF)
line1        db 27, "[1;32mHi, what group would you like to join?", 13, 10, "$" 
line2        db 27, "[1;34mThe Founders", 13, 10, "$"
line3        db 27, "[1;31mThe Vox Populi", 13, 10, "$"
line4        db 27, "[1;5;31;47mand maybe I'd want another sentence that blinks?$" 

.code
begin:  
    mov ax,@data
    mov ds,ax
    mov es,ax
    mov ah,09h
    lea dx,line1      ; print line 1 (bright green)
    int 21h
    lea dx,line2      ; print line 2 (bright blue)
    int 21h
    lea dx,line3      ; print line 3 (bright red)
    int 21h
    lea dx,line4      ; print line 4 (blinking bright red on white background)
    int 21h
    lea dx,a_reset    ; reset colors back to default before exiting
    int 21h
    .exit
end begin

Blinking is known to work in DOSBox, VMWare, Bochs, but doesn't work in emu8086, and Windows NT+ DOS prompt.

In a previous set of comments in this posters other related question I did suggest the alternative is to write a procedure or function that takes a string and uses DOS interrupts and/or BIOS interrupts to change the attributes on a character by character basis and display them to the screen (while keeping track of the cursor). The most efficient way is to directly write the attributes to video memory (ie 0xb000:0). The ANSI.SYS method above I have used is the easiest to get going, and should work by default in modern versions of DOSBox.

Community
  • 1
  • 1
Michael Petch
  • 46,082
  • 8
  • 107
  • 198