Ozzah
Ozzah

Reputation: 10701

ASM subroutine to print coloured text at specific location on the screen

I'm trying to write some ASM code that will write some text to the display using BIOS interrupts. This code will run from the boot sector.

I have

msgText DB "Hello"                  ;Text
msgCol  DB 0x07,0x08,0x09,0x0A,0x0B ;Colours
msgXY   DW 0x0E26                   ;Col/Row
msgLen  DB 0x05                     ;Length

The message is just "Hello", each letter having a different colour. The position of the message on the screen is roughly in the middle, and it has a length of 5.

I want to write a function that will write any message/colour/xy/length to the screen, but for now, let's focus on this one.

print:
  MOV AH,0x02        ;Tell BIOS we want to set the cursor position
  MOV DX,[msgXY]     ;Tell BIOS where the cursor should go
  INT 0x10           ;Call BIOS video interrupt

  XOR ECX,ECX        ;Clear ECX
  MOV CX,[msgLen]    ;Set CX = msgLen

  MOV AH,0x0E        ;We want to print characters on the screen

  _loop:
    MOV EDX,msgText  ;Move address of text to EDX
    DEC ECX          ;Temporarily decrement ECX
    ADD EDX,ECX      ;Add ECX to the address of msgText
    INC ECX          ;Increment ECX back to what it was
    MOV AL,BYTE[EDX] ;Put the contents of the memory at EDX into AL

    MOV EDX,msgCol   ;Move address of text colour to EDX
    DEC ECX          ;Temporarily decrement ECX
    ADD EDX,ECX      ;Add ECX to the address of msgCol
    INC ECX          ;Increment ECX back to what it was
    MOV BL,BYTE[EDX] ;Put the contents of the memory at EDX into BL

    INT 0x10         ;Call BIOS video interrupt
    LOOP _loop
  RET

Now, I should be able to call this with CALL print.

(yes, I know this prints "Hello" backwards, I'm not worried about that. I can just store it in memory as "olleH", I don't care - I've got much bigger problems)

First of all, the line where I set CX to [msgLen] causes problems. I've had to hardcode it to make CX equal to 0x05 and I'm not sure why.

Secondly, presumably for the same reason, when I try to set the colour code I have the same problem as the text length. If I get rid of those lines and just hardcode MOV BL,0x0C, then it works fine.

Oddly, however, the MOV DX,[msgXY] part seems to work OK.

I can't give any exact problems, because I've been trying to fix this for hours by changing lots of thing, and little things I change here and there seem to have very unpredictable results. Furthermore, calling various BIOS interrupts seems to change some of the registers, which is very annoying. Also, when I push stuff down onto the stack and pop it back out later, it's different, so I've been avoiding PUSH and POP altogether.

By the way, I'm still in real mode x86 at this stage.

Surely what I'm doing can't be this hard. Maybe I just need a coffee. Or some help? :)

Upvotes: 1

Views: 2601

Answers (2)

Srs
Srs

Reputation: 36

You Can Use This Code:

mov si,msg_text
call print_colored
print_colored:
.loop:
lodsb
cmp al,0
je .done
inc bl
mov ah,0x0E
Int 0x10
jmp .loop
.done:
ret

It makes your String colored but not for your choice.

Upvotes: 0

Frank Kotler
Frank Kotler

Reputation: 1452

Well, "first of all", you've defined msgLen as db. When you mov cx, [msgLen], you load two bytes. Whether this causes a problem depends on what (if anything) is after msgLen. Either define msgLen as dw, or do mov cl, [msgLen] (since you've got the upper bits cleared).

I don't see a similar problem with "Secondly" - you're moving a byte into an 8-bit register. Seems strange that you're using 32-bit registers in this section. "Should" work, but it bloats up your code a bit. [dx] is not a valid 16-bit addressing mode, of course - you'd have to use bx (which is being used for other purposes), si, or di. Since you are using 32-bit registers, lea edx, [edx + ecx - 1] should do what you want without temporarily decrementing and incrementing ecx. (but I don't think that'll help your problem)

It is expected that certain BIOS interrupts would alter registers. Whether it's weird or not depends on which interrupts are doing it. Mostly they don't. Not being able to pop the same value you pushed is weird. Either you've discovered a CPU bug or you're doing something wrong. Guess which is more likely. :) They're useful instructions, so it's a shame not to be able to use 'em. You don't show "act I" of your bootsector, where you initialize ds and es and set up a sensible stack - perhaps there's a problem there?

Have you looked at int 10h/13h? I think it'll do what you want ("rainbow" text). It's a weird interrupt, in that it expects the text address to be in es:bp, but useful. Another approach is to poke character and color directly to "screen memory" at B800h:xxxx - one byte for character and one byte for color.

A bootsector isn't particularly easy to write. If you've got real dos available (or even Dosbox), it may help to "try out" your routines as part of a .com file (where DEBUG is available). A bootsector is a different environment from dos, so it won't solve all your problems, but may help to narrow it down.

Coffee may help, but mostly... Courage!

Best, Frank

Upvotes: 2

Related Questions