Search code examples
arraysstringassemblytasm

Print into screen array with character GUI TASM Assembly


Good afternoon, I'm trying to show on screen an array of strings with GUI Turbo Asembler TASM, the problem that I can not show the all strings only the first. If someone can help me correctly display the strings on the screen and move through that array, very grateful-

This an example in Borland C++

Example

This actually in TASM:

Program in tasm

the code is the following.

.MODEL small

.STACK  100h ; reserves 256 bytes of uninitialized storage

.DATA
startX equ 35
startY equ 8
y db ?
x db ?
t1 db ?
t2 db ?
t3 db ?

zSprite db'M','M','L','E','E','N','A','E','V','E',
    db'E','R','H','O','N','G','O','S','T','R',
    db'X','X','O','T','I','R','R','A','C','A',
    db'I','S','A','P','P','O','T','A','P','S',
    db'C','C','M','L','A','A','I','Z','O','T',
    db'O','A','A','U','A','N','U','L','P','U',
    db'S','O','M','B','R','E','R','O','M','P',
    db'C','N','E','A','R','R','I','I','O','O',
    db'W','O','J','E','N','O','C','P','Z','E',
    db'A','A','Z','A','A','L','N','Y','T','D'


.386 ;enabled assembly of non privileged 80386 instructions
.CODE
start:
;set DS to point to the data segment
mov ax,@data
mov ds,ax

mov di,offset zSprite

mov y,0

l5:
cmp y,10
jl  l0
jmp l1

l0:
mov x,0

l4:
cmp x,10
jl  l2
jmp l3

l2:
mov al,startX
add al,x
mov t1,al

mov al,startY
add al,y
mov t2,al

; set cursor position at (x,y)
mov ah,02h ;set cursor position service
mov bh,00h ;page number
mov dh,t2 ;row
mov dl,t1 ;column
int 10h ;bios interrupt

mov ax,0 ;reset ax
mov al,y ;ax = y
mov bx,10
mul bx   ;ax = ax * 10
mov bx,0 ;reset bx
mov bl,x ;bx = x
add ax,bx ;ax = ax + x
mov bx,ax

; set color
mov ah,09h ;service
mov al, zSprite;character
mov bh,00h ;page number
mov bl,[bx+di] ;color
mov cx,01h ;number of times to print character
int 10h

;print symbol
mov ah, 02h  
mov dl,  zSprite
int 21h 

inc x
jmp l4

l3:
inc y
jmp l5

l1:
nop

exit:
;DOS: terminate the program
mov ah,4ch ; mov ax, 4c00h
mov al,0h
int 21h

delay PROC
pop bx

mov ax,1000d
mov dx,ax

delay1:
mov cx,ax

delay2:
dec cx
jnz delay2
dec dx
jnz delay1

push bx

ret
delay ENDP

END start

Solution

  • Hmm.. I decided to write somewhat advanced version of the display board... I know the pure-code answers are not good ones, but I added many comments into the code to make it more clear, how it works.

    Some hints about concepts used:

    I'm writing directly into VGA text video memory, avoiding BIOS/DOS services (they are slow and cumbersome to use in cases like drawing game board).

    The board data contain not only letters, but the top bit (80h value) of each "letter" is used as used/unused marker. The drawing routine will change the colour of letter based on the value of this bit.

    I.e. value 41h in board will work as "unused A", and value 41h + 80h = 0C1h will work as "used A".

    Unused/used letters have light_magenta/white colour, being calculated from the used bit and exploiting also the 40h bit of letter ASCII value. (digits would have bright_red/yellow colours, as '0' = 30h, so ASCII code of digits does not contain 40h bit set = different colour calculation result).

    The cursor is "drawn" + "hidden" by adding/subtracting colour to the original letter colour.


    And the wall of code (tested with TASM 4.1 under dosbox):

    .MODEL small
    
    .STACK  100h ; reserves 256 bytes of uninitialized storage
    
    .DATA
    
    BOARD_SIZE_X    EQU     10
    BOARD_SIZE_Y    EQU     10
    START_X         EQU     35
    START_Y         EQU     8
    CURSOR_COLOR    EQU     0B0h     ; add "blink" + cyan background
    
    board LABEL BYTE
        DB "MMLEENAEVE"
        DB "ERHONGOSTR"
        DB "XXOTIRRACA"
        DB "ISAPPOTAPS"
        DB "CCMLAAIZOT"
        DB "OAAUANULPU"
        DB "SOMBREROMP"
        DB "CNEARRIIOO"
        DB "WOJENOCPZE"
        DB "AAZAALNYTD"
    
    cursor_x        db  5
    cursor_y        db  7
    
    .386
    .CODE
    start:
        ;set DS to point to the data segment
        mov     ax,@data
        mov     ds,ax   ; ds = data segment
        mov     ax,0B800h
        mov     es,ax   ; es = text VRAM segment for direct VRAM writes
    
        ; fake some characters being "used" to test drawing code
        or      BYTE PTR [board+34],80h     ; mark the "POT" word
        or      BYTE PTR [board+35],80h     ; on fourth line in middle
        or      BYTE PTR [board+36],80h
    
        call    clear_screen
        call    draw_board
        mov     dl,CURSOR_COLOR
        call    draw_cursor
    
        ; wait for keystroke
        xor     ah,ah
        int     16h
    
        ; fake "move cursor"
        mov     dl,-CURSOR_COLOR        ; hide cursor on old position
        call    draw_cursor
        inc     BYTE PTR [cursor_x]     ; move it up+right
        dec     BYTE PTR [cursor_y]
        mov     dl,CURSOR_COLOR         ; show cursor on new position
        call    draw_cursor
        ; (other option would be to redraw whole board)
    
        ; wait for keystroke before exit
        xor     ah,ah
        int     16h
        ; exit to DOS
        mov     ax,4C00h
        int     21h
    
    ; sets whole text video RAM to white "space" with red background
    ; modifies ax, cx, di, assumes es=B800
    clear_screen PROC
        xor     di,di   ; B800:0000 target address
        mov     ax,' ' + 4Fh*256 ; white space on red background
        mov     cx,80*25
        rep stosw       ; fill up video RAM with that
        ret
    ENDP
    
    ; redraws whole board to the video RAM, marks "used" characters too
    ; modifies ax, cx, dx, si, di, assumes ds=@DATA, es=B800
    draw_board PROC
        mov     si,OFFSET board ; si = address of first letter of board
        ; di = offset of starting position in video RAM
        ; 2 bytes per char (char+color), 80 chars (160B) per line
        mov     di,(START_Y*80 + START_X)*2
        ; output BOARD_SIZE_Y lines
        mov     dx,BOARD_SIZE_Y
    board_line_loop:
        ; output BOARD_SIZE_X coloured characters
        mov     cx,BOARD_SIZE_X
    board_char_loop:
        lodsb           ; al = next character + used bit, advance si +1
        mov     ah,al   ; color of unused/used will be: 12 + 1 || 3 = 13 || 15
        and     al,7Fh  ; clear the top bit (used/unused): al = ASCII letter
        shr     ah,6    ; ah = 1 || 3 (80h "used" bit + 40h bit from letter code)
        add     ah,12   ; ah = 13 || 15 by "used" bit (magenda/white on black)
        stosw           ; write letter+color to VRAM es:di, advance di +2
        dec     cx
        jnz     board_char_loop ; loop till whole line is displayed
        ; move video ram pointer to start of next line
        add     di,(80-BOARD_SIZE_X)*2  ; advance to start of next line
        dec     dx
        jnz     board_line_loop ; loop till every line is displayed
        ret
    ENDP
    
    ; Modifies letter color at current cursor position by adding DL
    ; modifies ax, di, assumes ds=@DATA, es=B800
    draw_cursor PROC
        mov     al,[cursor_y]
        mov     ah,160
        mul     ah      ; ax = cursor_y * 160
        movzx   di,BYTE PTR [cursor_x] ; di = zero-extended cursor_x
        add     di,di   ; di *= 2 (cursor_x*2)
        add     di,ax   ; di += cursor_y * 160
        ; add initial board offset and +1 to address attribute only
        add     di,(START_Y*80 + START_X)*2 + 1
        add     es:[di],dl  ; modify letter color by adding DL
        ret
    ENDP
    
    END start
    

    Commands used to build exe:

    REM source file has name: wordgame.asm
    tasm /m5 /w2 /t /l wordgame
    tlink wordgame.obj
    

    Use Turbo debugger to single step over instructions, and watch how they affect CPU state and how they modify memory (set in Options the screen to "swap always", to make the direct video RAM writes visible on user screen (Alt+F5)). Try to understand everything, also your old code, how it works and where it had problems.