Graphics mode in assembly 8086
Asked Answered
U

1

13

I have a variable that is called average and in my DATASEG, it changes every time because the user enters a different input every time. What I want to do is to go to the graphics mode (VGA) and then print there Your average is: and then the average I know how to change to the graphics mode like this:

mov ax, 13h  
int 10h

After printing the average I want to print below if the average is above 75 You are a good student, keep up the good work and if not. Don't worry you will get better! Thanks in advance.

Unshackle answered 6/2, 2018 at 17:20 Comment(10)
Yes, it is possible. Will it be easy? Probably not anymore. Odds are that modern systems may not even allow you to enter a VGA mode anymore. You need to look online and dig around for a bit to see what is out there for this. FYI, going to mode 13, I don't think you have a font available and would need to write your own. Or, just use standard output to a text mode and have it done much easier.Jarrod
So what is your question? Really "is it possible"? Guess what, check the thousands of existing executables, I'm pretty sure some of them are doing very similar things, or lot more complex, so why do you even ask. If "DOOM" was possible in 1993, how much harder can it be to display "you are good student" ... You need platform which supports the 13h mode, if you insist on it (it's very simple mode, easy to learn, but 320x200 is very low resolution and texts are especially ugly in it, reconsider rather using some SVGA mode like 1024x768 (but after you learn to handle 13h)). Emu8086 allows 13h gfxSpade
A nice resource overview on VGA.Colombo
Thank you guys. I don't really mind if the text would be ugly it's for a project and I just want to try using graphics mode for the first time.Unshackle
what exactly you do not know how to do ... using 1. VGA 320x200x256 mode? 2. print character/text in it? 3. print number value (in register or memory) ? Font can be obtained from EGA/VGA BIOS or use this Convert floating-point numbers to decimal digits in GLSL?Adolphus
orthography is not optional. It's barely possible to read your questionModulation
@TomerCahal added answer ... to notify user nick you have to add @nick to the comment and site will do the restAdolphus
@spektre Please I need help in this. I already have an idea now. I will take the average which is in hexadecimal and make it decimal so I will have the original tens digit in decimal and one's digit and I already finished it now I change to the graphics mode and I want to print Your average is. Should I do pixel by Pixel I think that would be really hard. Also I can make 9 cases for each digit that i might print for the average. Thanks for the help, if something isn't clear please don't get mad just ask and i'll try my best to answer you guysUnshackle
@TomerCahal in video mode you need to do pixel by pixel. In text mode you can copy strings or even DOS interrupt for printing. You can also link in C++ sprintf routine and use that. What do you mean by take advantage of HEX for printing DEC ? that does not make any sense did you mean BCD (binary coded decimal) instead? add code what you have to your question with exact specification of your problem ... If I where you I would start with printing single char at specific position on screen and when working only then move to another problem ....Adolphus
btw you can try to port this C++ function to asm void numprn(word x,word y,word n); it does exactly what you want to do in 320x200x256c...Adolphus
A
16

I am assuming: PC VGA x86 MS DOS platform

It does not really matter if real or emulated unless you want low level IO access which might not work properly on emulation like DOSBOX ...

  1. Video/Text modes

    So to switch between video and text modes you need to use VGA BIOS:

    mov ax,mode ; here select which mode you want
    int 16      ; this calls EGA/VGA/VESA BIOS
    

    There are many video modes here two very important ones:

    mode | type  | segment | resolution         | align
    ----------------------------------------------------
    03   | text  | B800h   | 80x25 chars        | 2 Byte
    19   | video | A000h   | 320x200x256 colors | 1 Byte
    

    In video mode 19 you print/peek pixel by accessing memory at segment A000h where offset is computed like this:

    offset = 320*y + x
    

    The 320x200 mode fits entirely into 64 KByte segment so you do not need to switch pages. This makes it ideal for simple asm gfx programs ....

    Mode 3 is text mode where each character has 2 BYTEs one is color and the other is extended ASCII code. Again print/peek is done by accessing WORD at segment B800h where offset is:

    offset = (80*y + x) * 2
    

    Not sure what order is the two bytes in anymore it was ages ago but you can easily test if writing A at 0B800:0000 will render A in top left corner or 0B800:0001 instead. IIRC colors in text modes are just first 16 colors from palette and the color byte encodes ink paper brightness and flash. This text mode is also the default mode your MS-DOS shell is working in so you should set it back before program exit.

    So your program should look like this:

     start:
         mov ax,19 ; set video mode
         int 16      
    
     mainloop:
         ; here your stuff
    
     exit:
         mov ax,3
         int 16
         ret
    
  2. Printing strings

    For starters you can combine text and video modes ... Like I do here:

    it is a simple game where menus are in text mode (where printing is easy and just matter of copying the string into VRAM) and the sprite graphics game is on 320x200x256c video mode.

    When you want to print in gfx mode you first need to have some Font in the memory. If you look at the EGA/VGA BIOS documentation you can obtain the font located in EGA/VGA ROM and use directly that. I also created this image (IIRC using Trident 9000 256/512KB VGA font) which I use as a mono-spaced font for OpenGL and other stuff (where accessing VGA BIOS is not possible or wanted)...

    font

    Here GLSL example of using it for printing You can port it to CPU/VGA/asm but the printing on CPU is much more simpler no need for such horrible things like in GLSL fragment.

    So you just need to compute image position from ASCII code and copy its pixels into VRAM. Of coarse having bitmap in asm is not easy and much easier is to have it directly in binary form (as set of db) so you can write some simple C++ (or whatever) script which loads image and converts it to asm source ...

    Here is some ancient 320x200x256 colors printing lib I wrote in NASM ages ago (using EGA/VGA Font directly):

     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;GFX mode 13h print librrary ver:1.0
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;txti       init font adress
     ;char       cx=color,al=ASCII,scr:di<=al ;cl=ch => no background
     ;print      scr:di <= ds:si ,cx=color cl=ch => no background
     ;printl     scr:di text after call ,cx=color ...
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     txti:   pusha           ;init font adress
         push es
         mov ax,1130h    ; VGA BIOS - font info
         mov bh,3        ; font 8 x 8 pixels
         int 10h         ; ES:BP returns font address
         mov [cs:fonts],es   ;get font adr
         mov [cs:fonto],bp
         pop es
         popa
         ret
     fonts   dw 0        ; font address for printing ...
     fonto   dw 0
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     char:   pusha       ;cx=color,al=ASCII,scr:di<=al ;cl=ch => no background
         push    ds
         push    es
         push    word 0A000h
         pop es
         sub     ah,ah
         shl     ax,3
         mov     ds,[cs:fonts]
         mov     si,[cs:fonto]
         add     si,ax
         mov     dh,8
     .char0: mov     dl,8
         lodsb
         mov     ah,al
     .char1: mov     al,cl
         rcl     ah,1
         jc  .char2
         mov     al,ch
     .char2: cmp     cl,ch
         jz  .char3
         mov     [es:di],al
     .char3: inc     di
         dec     dl
         jnz     .char1
         add     di,320-8
         dec     dh
         jnz     .char0
         pop es
         pop     ds
         popa
         ret
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     print:  pusha       ;scr:di <= ds:si ,cx=color cl=ch => no background
     .l0:    lodsb
         or  al,al
         jz  .esc
         call    char
         add     di,8
         jmp     short .l0
     .esc:   popa
         ret
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     printl: mov [cs:.dat],si    ;scr:di text after call ,cx=color ...
         pop si
         push    ax
         push    di
         push    ds
         push    cs
         pop ds
     .l0:    lodsb
         or  al,al
         jz  .esc
         call    char
         add     di,8
         jmp     short .l0
     .esc:   pop ds
         pop di
         pop ax
         push    si
         add di,9*320
         mov si,[cs:.dat]
         ret
     .dat:   dw  0
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;;; end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    

    So to use it the program should be like:

     start:
         call txti ; just once at program startup
    
         mov di,50+320*10
         mov cx,127
         call printl
         db  'SPEKTRA software & hardware',0
    
         mov di,50+320*30
         mov cx,127
         call printl
         db  'print test',0
    

    The print is used by setting ds,si so it points to your null terminated string. As you can see printl does not need that as it uses string located directly after the printl call and program continues after it ... This way you do not need pointer setting instructions nor any additional labes ... The colors are in cl,ch one is ink and the other is paper. If cl==ch then no paper will be rendered just the ink pixels that is useful if you got image or gfx background behind the text ... The values for colors might not be visible I taken the colors from one of mine games which sets its own palette so if nothing is visible try to set different cl,ch like mov cx,0305h Take a look at this:

  3. Print numbers

    Printing non negative integer number value is a matter of dividing the number by base (10) and printing the remainder + '0' in reverse order as characters ...

    In hex it is even easier as each digit corresponds to nibble <0-15> so for 16 bit in you take highest 4 bits convert to char either by xlat table or by adding '0' or 'A' depending if value is below 10 ... so no divisions just bit shift/mask ... print the char and shift left the value by 4 bits to process next digit ...

    btw in gfx modes is often much nicer and user friendly to instead of printing a value as a number render a progress bar like stuff instead which is much much more easier ... collapses to single loop rendering H or V line ... like REP STOSB :) ...

  4. VESA

    For Super VGA video modes above 320x200x8bpp we need to add page crossings of data as VRAM does not fit 64KByte segment anymore. I strongly suggest to use VESA (VBE) api for this. The idea is to have the A000:0000 segment be mapped to certain segment of the VRAM which we can change at any time. For more info on how see these:

    In case your gfx card does not have VESA/VBE for most cards its possible to add it by using UniVBE 5.3 or newer utility under MS-DOS.

Adolphus answered 7/2, 2018 at 12:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.