Printing strings in graphics mode (320x200)

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Benjamin1996
Member
Member
Posts: 78
Joined: Sat Apr 10, 2010 7:00 am
Location: Denmark

Printing strings in graphics mode (320x200)

Post by Benjamin1996 »

Hi folks!
Lately I switched to graphics mode number 13h.
Before I did this, I was aware this would require me to plot pixel by pixel for every graphical operation I did. Yet, I was silly enough to think that I could just switch back to text mode whenever I wanted, print some stuff, and still have my graphics on the screen. Making the discovery that this would just clear all graphics off the screen, made it clear to me I had to find a way to print text in graphics mode.
Even though I have a pretty clear idea of what this implies, I can't seem to find a resource that explains nor shows this.
So I would really appreciate if any of you could direct me to a bare-bones NASM example of this. - That is, printing text in 320x200 graphics mode ;).

-Benjamin.
User avatar
piranha
Member
Member
Posts: 1391
Joined: Thu Dec 21, 2006 7:42 pm
Location: Unknown. Momentum is pretty certain, however.
Contact:

Re: Printing strings in graphics mode (320x200)

Post by piranha »

Well, theres http://wiki.osdev.org/Drawing_In_Protected_Mode. It's not nasm, it's in C. However, it gives a very clear way of printing strings in graphics mode...it's pretty easy. Start at one spot on the screen, and simply move across and down checking a font encoding array as you go to see if a pixel should be there. It's really pretty easy.

-JL
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
Benjamin1996
Member
Member
Posts: 78
Joined: Sat Apr 10, 2010 7:00 am
Location: Denmark

Re: Printing strings in graphics mode (320x200)

Post by Benjamin1996 »

Thanks a lot for the quick reply piranha :). Unfortunatly I already read the article you linked to, and I don't seem to understand the concept, at all :(.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Printing strings in graphics mode (320x200)

Post by Combuster »

Time for a pop quiz then:

1: what is a bitmap?
2: how can you get the bit at position (x,y) from that bitmap?
3: what is the desired foreground and background color?
4: how do you draw a pixel at (x,y)
5: if you start a bitmap at (x,y), what point on the screen corresponds to point (a,b) in the bitmap?
6: how do you iterate over all points in a bitmap?
7: when would you use the foreground color, when would you use the background color, and when would you not draw at all?
8: how would you store 256 bitmaps of 8x16 pixels?

Once you've got all the answers, try to fit the pieces together :wink:
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Benjamin1996
Member
Member
Posts: 78
Joined: Sat Apr 10, 2010 7:00 am
Location: Denmark

Re: Printing strings in graphics mode (320x200)

Post by Benjamin1996 »

Combuster wrote:Time for a pop quiz then:

1: what is a bitmap?
2: how can you get the bit at position (x,y) from that bitmap?
3: what is the desired foreground and background color?
4: how do you draw a pixel at (x,y)
5: if you start a bitmap at (x,y), what point on the screen corresponds to point (a,b) in the bitmap?
6: how do you iterate over all points in a bitmap?
7: when would you use the foreground color, when would you use the background color, and when would you not draw at all?
8: how would you store 256 bitmaps of 8x16 pixels?

Once you've got all the answers, try to fit the pieces together :wink:
As far as I'm concerned a bitmap representing a letter is just a two-dimensional array, that can be accesed through an index, like a normal array. Then you should simply loop through every index in that 2D array, check if the bit at that location is 1, if it is, draw a pixel on the correct screen location ((yIndex * 640) + xIndex), if it's a 0 simply skip and go to the next index. Is this correct? - If it is, then how would you define a bitmap in NASM assembler?
Oh and by the way, I also switched to mode 12 (640x480)... hence the above equation ;).
fronty
Member
Member
Posts: 188
Joined: Mon Jan 14, 2008 5:53 am
Location: Helsinki

Re: Printing strings in graphics mode (320x200)

Post by fronty »

Did you check the manual?
User avatar
Neolander
Member
Member
Posts: 228
Joined: Tue Mar 23, 2010 3:01 pm
Location: Uppsala, Sweden
Contact:

Re: Printing strings in graphics mode (320x200)

Post by Neolander »

Benjamin1996 wrote:As far as I'm concerned a bitmap representing a letter is just a two-dimensional array, that can be accesed through an index, like a normal array. Then you should simply loop through every index in that 2D array, check if the bit at that location is 1, if it is, draw a pixel on the correct screen location ((yIndex * 640) + xIndex), if it's a 0 simply skip and go to the next index. Is this correct?
No, simply skipping is not correct, because it only works if you assume that the screen is initially black.
If there's already a white pixel (or whatever color suits your taste) at this location, skipping will result in this pixel remaining white, while you want it to become black if you do not aim for translucency.
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Re: Printing strings in graphics mode (320x200)

Post by Creature »

IMHO having a text-mode-like-interface in graphics mode has a couple of things that make it substantially more difficult than normal text mode, such as:
  • You can't JUST move the cursor around, if you put it on a character and have to move it out, you will probably need to redraw parts of that character because it was partially overwritten.
  • For scrolling around in text mode, you can just keep a buffer of 2-byte integers (e.g. words), but in graphics mode, parsing an entire buffer full of characters (with, possibly a lot of colors shipped with them) and drawing them on the screen is pretty slow (and you can't just memcpy like you can in text mode). Usually you'll need to redraw the entire screen too (because everything moves up or down), which can be a performance hit. Better get those graphics blitters in shape!
  • When will you redraw the screen? After plotting each character? That will probably give you a noticeable slowdown (especially in emulators, I've noticed). You could redraw after a shell command has finished, but what if the shell command waits for input during the process? The text will need to be redrawn before that. You could also have a thread that updates the screen every once in a while, but threads mean overhead, so...
  • You'll need some sort of text-mode font that fits the resolution (or you need to skip a few pixels at the end of each character row).
I had a small text-mode interface in graphics mode a while back, but decided to turn it off for now (just because damn, text mode is FAST in comparison ;), and also because I didn't really trust my code enough to do everything in a stable way).
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
Benjamin1996
Member
Member
Posts: 78
Joined: Sat Apr 10, 2010 7:00 am
Location: Denmark

Re: Printing strings in graphics mode (320x200)

Post by Benjamin1996 »

Thanks a lot for all of the quick replies folks! :)
I think I'm going to take use of extern function calls in Assembly (calling the C function, printChar, from my bootloader). Simply because I think a such function would be a lot easier to write in C, but of course that requires a 16-bit C compiler and linker. Does anybody have any suggestions? :D
EDIT: Oh sorry, I clearly didn't make any research before saying I had to get a 16-bit C compiler & linker. I got it working with GCC (32-bit). The only thing I'm uncertain of is how to pass arguments to the C function. I know C uses the stack for parameters, but I'm uncertain of how I can restore the stack after the function has been executed. As far as I'm concerned, C pops the parameters of the stack automatically, so all I have to do it to restore the stack pointer. Sooooo, the thing I'm confused of, is this example:

Code: Select all

global  _main

extern  _printf

section .data

text    db      "291 is the best!", 10, 0
strformat db    "%s", 0

section .code

_main
        push    dword text
        push    dword strformat
        call    _printf
        add     esp, 8
        ret
If stating that C automatically pops its parameters is correct, I suppose it automatically gets rid of 'text' and 'strformat'?
What confuses me, is this line:

Code: Select all

add esp, 8
Is there some particular reason it adds 8 to the stack pointer? -Or is this just always the case? (Sorry if my question is stupid).
Benjamin1996
Member
Member
Posts: 78
Joined: Sat Apr 10, 2010 7:00 am
Location: Denmark

Re: Printing strings in graphics mode (320x200)

Post by Benjamin1996 »

I've put together a first try at making a printA function. Unfortunatly, I can't call it a successful first try, because nothing is displayed. Here's the "main" routine from boot.asm:

Code: Select all

main:
    xor ax, ax
    mov ds, ax
    
    call mode12h
    call clearScreen

    mov al, 10
    mov dx, 10
    mov cx, 10
    call printA
    
    jmp $
This is the "printA" function, from graphics.inc:

Code: Select all

extern _printCharA
printA:
    call _printCharA
    ret
"plotPixel" function:

Code: Select all

global _plotPixel
_plotPixel:
    push ebp
    mov ebp, esp
    mov ah, 0x000c
    int 0x0010
    leave
    ret
This is all the functions in graphics.c:

Code: Select all

void addX(int x){
	asm("push %ax");
	asm("movw %0, %%ebx" : : "a" (x));
	asm("add %bx, %cx");
	asm("pop %ax");
}

void addY(int y){
	asm("push %ax");
	asm("movw %0, %%ebx" : : "a" (y));
	asm("add %bx, %dx");
	asm("pop %ax");
}

void printCharA(){
    int y;
	int x;

	for(y = 0; y < 8; y++){
		for(x = 0; x < 8; x++){
			if(aFont[y][x] == 1){
				int yLocation = y * 640;
				addY(yLocation);
				addX(x);
				plotPixel();
			}
		}
	}
}
"aFont" is just a two-dimensional integer array, forming an A out of 1s and 0s.
This is assemble.bat:

Code: Select all

@echo off
nasm -f elf "../boot/boot.asm" -o "../temp/boot.elf"
"C:\MinGW\bin\gcc.exe" -c "../boot/graphics.c" -o "../temp/graphics.o"
ld -Ttext 0x7c00 "../temp/boot.elf" "../temp/graphics.o" -o "../temp/boot.o"
objcopy -O binary "../temp/boot.o" "../temp/boot.bin"
pause
And this is what I get running assemble.bat:

Code: Select all

C:\Users\Benjamin\AppData\Local\Temp/ccmsTYSI.s: Assembler messages:
C:\Users\Benjamin\AppData\Local\Temp/ccmsTYSI.s:81: Warning: using `%bx' instead
 of `%ebx' due to `w' suffix
C:\Users\Benjamin\AppData\Local\Temp/ccmsTYSI.s:81: Warning: using `%ax' instead
 of `%eax' due to `w' suffix
C:\Users\Benjamin\AppData\Local\Temp/ccmsTYSI.s:97: Warning: using `%bx' instead
 of `%ebx' due to `w' suffix
C:\Users\Benjamin\AppData\Local\Temp/ccmsTYSI.s:97: Warning: using `%ax' instead
 of `%eax' due to `w' suffix
Press any key to continue . . .
I know the code is pretty much pathetic, but it's just to get somewhat like my first A printed in graphics mode.
And as said, the above code doesn't produce any screen output.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Printing strings in graphics mode (320x200)

Post by Combuster »

Please don't put C code in a bootsector unless you know exactly what you are doing. Chances are you either screw up your C environment, or overrun the 512 byte boundary in trying to do so. Grab a debugger and you'll notice that what you coded does not get executed.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Benjamin1996
Member
Member
Posts: 78
Joined: Sat Apr 10, 2010 7:00 am
Location: Denmark

Re: Printing strings in graphics mode (320x200)

Post by Benjamin1996 »

Alright everyone, I'm back with some updates :).
I trashed the C idea (under reasonable recommendations from Combuster). And decided to do it in NASM. I plan on storing the fonts in simple arrays, and pretty much do it the same way I did it in C. Yet, I've run into complications right from the beginning. I can't seem to get a simple array working. I know it sounds retarded.. 8).
Here's the 'aFont' array I surprisingly enough plan on using for my A font.
Here it is from graphics.inc so far: (Just a test array, to see if I could even make one)

Code: Select all

aFont:
    db 5

And here's what I do to print out the 5 stored in the array (again, just for testing purposes):

Code: Select all

	mov ah, 0x000e
	mov al, aFont
	add al, 0x0030
	mov bh, 0x0000
	mov bl, 0x0007
	int 0x0010
But this doesn't print out a 5, it prints a 3?? Moving aFont+1 into al, prints a 4 etc etc.. why?
EDIT: This was actually as retarded as it sounds. I turns out I forgot to put square brackets around aFont to refer to the value pointed to by the memory.
Sorry for wasting your time. I'll return once I wrote some code to (hopefully) print an A.
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: Printing strings in graphics mode (320x200)

Post by Gigasoft »

You can retrieve the standard font from the BIOS using the following service, in case you don't feel like making your own:
http://www.ctyme.com/intr/rb-0158.htm
Benjamin1996
Member
Member
Posts: 78
Joined: Sat Apr 10, 2010 7:00 am
Location: Denmark

Re: Printing strings in graphics mode (320x200)

Post by Benjamin1996 »

Gigasoft wrote:You can retrieve the standard font from the BIOS using the following service, in case you don't feel like making your own:
http://www.ctyme.com/intr/rb-0158.htm
Thanks a bunch Gigasoft! You're a lifesafer! :)
So I could just move 0x1130 into ax, and then let's say 6h into bh to get a 8x16 font, and then make a call to interrupt number 10h and then I'll have the font in ES:BP?
EDIT: I did some research on the subject and it seems that it leaves an offset into the ASCII table (starting at 0) of the selected character set, is this correct?
EDIT2: I've run into some implementation issues... I thought once I had set ax to 0x1130, bh to 0x0002 (8x14 font) & made a call to int 0x0010, I could use ES:BP as an offset (starting at 0) into the ASCII table, so that this code:

Code: Select all

	mov ah, 0x000e
	mov al, [es:bp + 65]
	mov bh, 0x0000
	mov bl, 0x0007
	int 0x0010
Would print a capital A in 8x14 font, instead it printed out some weird symbol. Here's a screenshot:
Attachments
symbol.PNG
User avatar
Creature
Member
Member
Posts: 548
Joined: Sat Dec 27, 2008 2:34 pm
Location: Belgium

Re: Printing strings in graphics mode (320x200)

Post by Creature »

Or you could use a bitmap version of the Bochs font instead and load that from disk (or through multiboot modules). But it's probably faster to do it through the BIOS (no disk loading involved, so no errors can occur following from that).
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.
Post Reply