Thursday, June 28, 2007

Drawing on screen (Part 1)

Task Details:
DRAW your college roll number on the screen. Examples are

BCSS05M012


OR

B C S
S 0 5
M 0 1 2

Solution
.model small
.stack 100h
.data
.code

pHorizontalLine proc

; SI = X1
; DI = X2
; DX = Y
; BL = LineWidth
; AL = Color

; X1 should be smaller than X2

cmp si, di ; Compare si (X1) with di (X2)
jbe ok1 ; if smaller or equal, ok
xchg si, di ; else, exchange values so X1 is smaller than X2

ok1:

mov ah, 0ch ; service number
mov bh, 0 ; page number

width1:

mov cx, si ; column number (X1)

start1:

int 10h ; Call the interrupt

cmp cx, di ; Check if the target (X2) has been reached
je checkwidth1

inc cx ; if not, increment the value

jmp start1 ; and loop back

checkwidth1:

; We have make the line wide
; by printing adjacent lines

inc dx ; move to next row
dec bl ; decrement the width

cmp bl, 0 ; and check the remaining width
je exit1 ; if its 0 then exit

jmp width1 ; otherwise loop back

exit1:

ret

pHorizontalLine endp

pVerticalLine proc

; SI = Y1
; DI = Y2
; CX = X
; BL = LineWidth
; AL = Color

cmp si, di
jb ok2
xchg si , di

ok2:

mov ah, 0ch
mov bh, 0

width2:

mov dx, si

start2:

int 10h

cmp dx , di
je checkwidth2

inc dx

jmp start2

checkwidth2:

inc cx
dec bl

cmp bl, 0
je exit2

jmp width2

exit2:

ret

pVerticalLine endp

pDiagonalLine proc ; \ or / line

; CX = X1
; DX = Y1
; SI = X2
; DI = Y2
; BL = LineWidth
; AL = Color

; Make X1 less than X2 first

cmp cx, si
jb ok3

xchg cx , si ; if X1 is swaped with X2
xchg dx, di ; swap Y1 with Y2 as well

ok3:

mov ah, 0ch ; Service number
mov bh, 0 ; Page number

width3:

push cx ; Save the value of cx (X1)
push dx ; and dx (Y1)

start3:

int 10h ; Call the interrupt

cmp cx, si ; Check if the line reached si (X2)
je checkwidth3 ; then stop printing the line

inc cx ; otherwise increment cx

cmp dx, di ; or if the line reached di (Y2)
je checkwidth3 ; then too; stop

jb less ; if the value of Y1 is less than Y2 ...

dec dx ; else Y should DECREMENT from Y1 to Y2

jmp start3 ; loop back

less:

inc dx ; ... then Y should INCREMENT from Y1 to Y2

jmp start3 ; loop back

checkwidth3:

; Line complete
; now make it wide

pop dx ; restore the value of X1
pop cx ; and Y1

inc cx ; For next line, move X1
inc si ; and X2 a point further

dec bl ; dec the total width of line

cmp bl, 0 ; if the width is zero
je exit3 ; then exit

jmp width3 ; loop for next line

exit3:

ret

pDiagonalLine endp


; Utility Macros
; to make the procedure call easy (one line)

mHL macro X1, X2, Y, LWidth ; Horizontal Line

mov si, X1
mov di, X2
mov dx, Y
mov bl , LWidth
call pHorizontalLine

endm

mVL macro Y1, Y2, X, LWidth ; Vertical Line

mov si, Y1
mov di, Y2
mov cx, X
mov bl, LWidth
call pVerticalLine

endm

mDL macro X1, Y1, X2, Y2, LWidth ; Diagonal Line

mov cx, X1
mov dx, Y1
mov si, X2
mov di, Y2
mov bl, LWidth
call pDiagonalLine

endm

main:

mov ah, 0h
mov al, 13h
int 10h

;============================
; B
;============================
mov al, 05 ; Set the color for the letter

; And draw
mVL 20, 60, 90, 10

mHL 100, 110, 20 , 5
mHL 100, 110, 38, 5
mHL 100, 110, 56, 5

mVL 30, 32, 110, 10
mDl 101, 20, 110, 30, 10
mDl 101, 42, 110, 32, 10

mVL 48, 50, 110 , 10
mDl 101, 38, 110, 48, 10
mDl 101, 60, 110, 50, 10
;============================
; C
;============================
mov al, 06

mVL 25, 55, 145, 10

mHL 155, 167, 20 , 5
mHL 155, 167, 56, 5

mDl 145 , 24, 155, 20, 10
mDl 145, 56, 155, 60, 10

mDl 158, 20, 168, 26 , 10
mDl 158, 60, 168, 54, 10
;============================
; S
;============================
mov al, 07

mHL 210, 220, 20, 5
mHL 210, 220, 38, 5
mHL 210, 220, 56, 5

mVL 28, 34, 200, 10
mVL 45, 53, 221, 10

mDl 214, 20, 224, 26, 10
mDl 214, 38, 224, 44, 10
mDl 214, 60, 224, 54, 10

mDl 200, 27, 210 , 20, 10
mDl 200, 35, 210, 42, 10
mDl 200, 54, 210, 60, 10
;============================
; S
;============================
mov al, 01

mHL 100, 110, 80, 5
mHL 100, 110 , 98, 5
mHL 100, 110, 116, 5

mVL 88, 94, 90, 10
mVL 105, 113, 111, 10

mDl 104, 80, 114, 86, 10
mDl 104, 98, 114, 104, 10
mDl 104, 120, 114, 114, 10

mDl 90, 87, 100 , 80, 10
mDl 90, 95, 100, 102, 10
mDl 90, 114, 100, 120, 10
;============================
; 0
;============================
mov al, 09

mVL 85, 115, 145, 10
mVL 85, 115 , 163, 10

mHL 155, 167, 80, 5
mHL 155, 167, 116, 5

mDl 145, 84, 155, 80, 10
mDl 145, 116, 155, 120, 10

mDl 158, 80, 168, 84, 10
mDl 158, 120, 168, 116, 10
;============================
; 5
;============================
mov al, 10

mHL 210, 230, 80, 5
mHL 210, 220, 98, 5
mHL 210, 220, 116, 5

mVL 80, 102, 200, 10
mVL 105, 113, 221, 10

mDl 214, 98, 224, 104, 10
mDl 214, 120, 224, 114, 10

mDl 200, 114, 210 , 120, 10
;============================
; M
;============================
mov al, 11

mVL 145, 180, 63, 10
mVL 145, 180, 83, 10
mVL 145 , 180, 103, 10

mHL 69, 107, 140, 5

mDl 63, 144, 68, 140, 10
mDl 99, 140, 104, 144, 10
;============================
; 0
;============================
mov al, 12

mVL 145, 175, 137, 10
mVL 145, 175, 155, 10

mHL 142, 159, 140, 5
mHL 142, 159, 176, 5

mDl 137, 144, 147, 140 , 10
mDl 137, 176, 147, 180, 10

mDl 150, 140, 160, 144, 10
mDl 150, 180, 160, 176, 10
;============================
; 1
;============================
mov al, 13

mVL 140, 144, 197, 5
mVL 145, 180, 192, 10

mDl 192, 144, 196, 140, 5
;============================
; 2
;============================
mov al, 14

mHL 238, 248, 140, 5
mHL 238, 248 , 158, 5
mHL 238, 258, 176, 5

mVL 158, 180, 228, 10
mVL 147, 155, 248, 10

mDl 227, 146, 237, 140, 10
mDl 241, 140, 251, 146, 10

mDl 241, 162, 251, 156, 10
;============================

; Wait for keypress
mov ah, 01h
int 21h

; Change graphics mode back
mov ah, 0h
mov al, 03h
int 10h

; and exit
mov ah, 4ch
int 21h

end main

Saturday, June 16, 2007

String Instructions' Exercise

String Instructions' Task

  1. Read a string from user using single character input service until the user presses enter character. Store the string in memory
  2. Copy this string to another memory location
  3. Retrieve the new string from memory and display it to the user in new line using single character output service.
  4. Take another single character input from user and find the number of occurrences of this character in the string.
  5. Input another string from the user and find the points of differences between this and old string.
Notes
  • The strings can not be more than 20 characters long.
  • All the task points are designed to make use of string instructions. Other ways to do these tasks are not valid.

Solution

.model small
.stack 100h
.data

string1 db 21 dup(0)
string2 db 21 dup( 0)

msg1 db "The character '", 0
msg2 db "' is repeated " , 0
msg3 db " time", 0
msg4 db "Please enter a string of 20 character at most: ", 0
msg5 db "Please enter the character to be found in the string: " , 0
msg6 db "Please enter another string of 20 characters at most: ", 0
msg7 db "The points of differences in both strings are ", 0

.code

; Macro to print a new line
mNewLine macro

mov ah, 02h
mov dl, 0dh
int 21h
mov dl, 0ah
int 21h

endm

; Procedure to input a string, 20 characters max
; Expects offset of memory in di
pStringInput proc

mov cx, 20 ; 20 charcters max

start1:

mov ah, 01h ; Take single character input
int 21h ; its in al now

cmp al, 0dh ; if its enter, exit
je exit1

stosb ; store the new character in the memory at di

loop start1

exit1:

ret

pStringInput endp

; Procedure to copy a string to another
; Expects offset of source in si and destination in di
pCopyString proc

mov cx, 20 ; 20 chars max
rep movsb ; copy from si to di
ret

pCopyString endp

; Procedure to display a null terminated string
; Expects offset of memory in si
pDisplayString proc

mov ah, 02h ; Single character output

start3:

lodsb ; load value in al from memory

cmp al, 0 ; if its 0 (null),
je exit3 ; exit

mov dl, al ; put the value in dl
int 21h ; and call the interrupt

jmp start3

exit3:

ret

pDisplayString endp

; Procedure to count occurences of some character in another string
; Expects the offset of memory in di
pCountChar proc

mov ah, 01h ; Get the character to be searched
int 21h

mov cx, 20 ; look in 20 charcters long string
mov dx, 0

start4:

repne scasb ; repeat if not found, stop if found
cmp cx, 0 ; either the character in found or cx is 0
je exit4 ; if cx is 0, means string finished, exit
inc dx ; otherwise, the character is found, increment dx to count it

jmp start4 ; go back and seacrh again until cx is 0

exit4:

dec di ; the last scasb had incremented di to 21st position, come back to 20th
cmp [di], al ; compare if the last character, when cx was zero, is same as we are searching for
jne skip ; no? skip

inc dx ; yes? count this one too

skip:

mov cl, al ; save the required character
mov ch, dl ; save the number of occurences

mNewLine

mov si, offset msg1
call pDisplayString

mov dl, cl ; print the character
mov ah, 02h
int 21h

mov si, offset msg2
call pDisplayString

mov ax, 0 ; print the number of occurences
mov al, ch
call pDecDisplay

mov si , offset msg3
call pDisplayString

ret

pCountChar endp

; Procedure to count number of difference between two strings
; Expects offsets in si and di
pPointsOfDiffs proc

mov dx, 0
mov cx, 20
start5:

repe cmpsb ; repeat until equal, stop when not equal
cmp cx, 0 ; either cx is 0 or the character are not equal
je exit5 ; if cx is 0, exit
inc dx ; count the point of difference

jmp start5 ; go back until cx is 0

exit5:

dec di
dec si
mov al, [si]
cmp [ di], al ; check if the last onew were different
je skip ; no? skip

inc dx ; yes? count this one as well

skip:

mov ax, dx ; display
call pDecDisplay

ret

pPointsOfDiffs endp

; Procduer to display a number in decimal
; Expect the number in ax
pDecDisplay proc

mov si, 10
mov cx, 0

start6:

mov dx, 0
div si
push dx
inc cx
cmp ax, 0

jne start6

mov ah, 02h

start7:

pop dx
add dl , 30h
int 21h

loop start7

ret

pDecDisplay endp

main:

; Set the data segment and extra segment
mov ax, @data
mov ds, ax
mov es, ax

; Display the message
mov si, offset msg4
call pDisplayString

; Input the string Task 1
mov di, offset string1
call pStringInput

; Come to new line
mNewLine

; Copy the string to new location Task 2
mov si, offset string1
mov di, offset string2
call pCopyString

; Display the new string Task 3
mov si, offset string2
call pDisplayString

; Display new line
mNewLine

; Print the message
mov si, offset msg5
call pDisplayString

; Count the number of occurences Task 4
; of some in the string
mov di, offset string1
call pCountChar

; new line
mNewLine

; Display message
mov si, offset msg6
call pDisplayString

; Get a new string input
mov di, offset string2
call pStringInput

; new line
mNewLine

; Display message
mov si, offset msg7
call pDisplayString

; Count the points of differences Task 5
; between the strings
mov si, offset string1
mov di, offset string2
call pPointsOfDiffs

; Exit
mov ah, 4ch
int 21h

end main

Friday, June 15, 2007

The Globician

Our monitor is much like a magic globe which we have been dreaming of long long ago! Today programmers are the magicians of this Globe. I met a magician of globe; The Globician who told me what I am telling you.

The front of monitor is coated on the inside with phosphorous which glows when electron strike it. On the rear end, it is equipped with 3 electron guns, red, green, and blue! electrons from respective guns produce the respective glow. These guns fire electrons on the instructions they receive. The instructions consists of the color as well as the position on which the color is to be produced.

For historical reasons, there are different modes in which you can display what you want on the monitor screen. The table summarizes these modes:

Mode Type Max Colors Text Resolution Graphics Resolution Max Pages Base Address
00 Text 16 40 × 25   8 B8000H
01 Text 16 40 × 25   8 B8000H
02 Text 16 80 × 25   4,8 B8000H
03 Text 16 80 × 25   4,8 B8000H
04 Graphics 4 40 × 25 320 × 200 1 B8000H
05 Graphics 4 40 × 25 320 × 200 1 B8000H
06 Graphics 2 80 × 25 640 × 200 1 B8000H
07 Text mono 80 × 25   1,8 B0000H
08 Graphics 16 20 × 25 160 × 200 1 B0000H
09 Graphics 16 40 × 25 320 × 200 1 B0000H
0A Graphics 4 80 × 25 640 × 200 1 B0000H
0D Graphics 16 40 × 25 320 × 200 8 A0000H
0E Graphics 16 80 × 25 640 × 200 4 A0000H
0F Graphics mono 80 × 25 640 × 350 2 A0000H
10 Graphics 16 80 × 25 640 × 350 2 A0000H
11 Graphics 2 80 × 25 640 × 480 1 A0000H
12 Graphics 16 80 × 25 640 × 480 1 A0000H
13 Graphics 256 40 × 25 320 × 200 1 A0000H

These are the basic video modes. Now how to set the video mode?

For video tasks, we have to use interrupt 10h.

The service number 0h allows you to change the video mode.

Here is the simple code to set the video mode to 13.

mov AL, 13h
mov AH, 0h,
int 10H

When we set the video mode, the computer display is calibrated horizontally and vertically in rows and columns. For mode 13, we have 320 columns and 200 rows. Each intersection of rows and columns gives a small square or rectangular area of screen which is known as pixel.

You can see your screen as an xy-plane. Rows are calibrated on y-axis and columns on x-axis. The pixels represents the points in xy-plane. The point to remember is that point (0, 0) is top-left corner of your screen, not bottom left.

Now that we have set the video mode, we want to draw something. Images you may know are in fact collection of colored dots. Here, we have dots (pixels) and we can color them as well. So, like an artist, we have to color the pixels in such a way that they represent images. How can I color a single pixel?

Service number 0CH of int 10H is the key. Put the color value in AL, column number in CX and row number in DX.

What color? How many color do we have in mode 13? 256! Which means 0 to 255 are the color numbers. Now which color number is your one? Playing with colors is the most easy way I can tell you. Lets set pixel (200, 100) to color 12.

mov AL, 12
mov AH, 0ch
mov CX, 200
mov DX, 100
mov BH, 0
int 10H

Well done me! Why did I set BH to 0? Its the page number. Lets put it aside for some time and continue with its value 0.

Well, we have put a colored pixel on the screen, what not can we do now! Start by painting a line, a horizontal line. Keep the value of row same and change the value of column in a loop and dont forget to call the interrupt each time. It will give you a simple line. Let see:

mov CX, 100
mov DX, 100
mov AL, 12
mov AH, 0ch
mov BH, 0
start:
int 10H
loop start

Here, I set CX and DX to 100 so the dot is positioned at (100, 100). AL has got the value of color, AH has service number and BH is 0. Now execution enters the loop where int 10H is called. This places a dot on the screen. The loop directive decrements the value of CX and it becomes 99. Now it checks the value of CX which not zero so it jumps to start. Again int 10H is called to print the dot. But this time the dot is at (99, 100)! CX is decremented! Try it!

Now that we have put a line on screen, we can draw different shapes as well. But what exactly happens when we draw things? In the table above, we have a base address in front of each mode. It is base address of the display memory. Our display commands are stored in memory and the computer screen shows whatever we put in the display memory.

For example in mode 13, the base address is A0000H. We have to display 256 different colors for each pixel. So we can have 8 bits to store a color number between 0 to 255; that is 1 byte. At a resolution of 320 × 200 we need 64000 such locations. So, starting from A0000H, all the 64000 bytes represent 64000 pixels in the screen. The BIOS interrupt calculates the byte number itself for you when you provide row and column number. How?

Pixel number = row number × number of pixels per row + column number

For example in mode 13, location (100, 100) would be:

100 × 320 + 100 = (32100)10 = 7D64H

This is the pixel number; the offset address. And the segment address is A000H. Thats how you put colors on the screen. If you directly put the colors on memory, this is called Direct Memory Access (DMA). Lets see the code for putting a dot on screen at (200, 100) of color 12 in both ways.

BIOS Version

.model small
.stack 100h
.data
.code
main:

; Change the video mode
mov ah, 0h ; Service number
mov al, 13h ; Mode number
int 10h ; Interrupt

; Put a dot on screen
mov AL, 12 ; Color Number
mov AH, 0Ch ; Service Number
mov CX, 200 ; Column Number
mov DX , 100 ; Row Number
mov BH, 0 ; Page Number
int 10H ; Interrupt

; Dot is on the screen, want to see it? Wait then!
mov ah, 01h ; With single character input
int 21h

; Change the video mode back to correct one
mov ah, 0h
mov al, 03h
int 10h

; Exit
mov ah, 4ch
int 21h

end main

DMA version

.model small
.stack 100h
.data
.code
main:

; Set the segment address to 0A000H
mov ax, 0A000H
mov ds, ax

; Change the video mode
mov ah, 0h ; Service number
mov al, 13h ; Mode number
int 10h ; Interrupt

; Put a dot on screen

; Column number = 200
; Row Number = 100
; Pixel Number = 100 × 320 + 200 = 32200

mov bx, 32200 ; Offset Address

; Put color on that location
mov al, 12
mov [bx], al

; Dot is on the screen, want to see it? Wait then!
mov ah, 01h ; With single character input
int 21h

; Change the video mode back to correct one
mov ah, 0h
mov al, 03h
int 10h

; Exit
mov ah, 4ch
int 21h

end main

Thursday, April 26, 2007

Concat Two User Provided Strings

.model small
.stack 100h
.data

; Its recommended to display a message to user before every input and output

; Message for first input
msg1 db "Enter the first string: $"

; Message for second input
msg2 db 0ah, 0dh, "Enter the second string: $"

; Message for third input
msg3 db 0ah, 0dh, "New string is: $"

; For each buffered input, we have to reserve space such that
tsize1 db 10        ; First byte contains a value that indicates the number of characters to be received at maximum
asize1 db ?         ; Second byte is reserved for interrupt's use
array1 db 10 dup(?) ; The third byte and on for saving the input. The minimum amount of space required
                    ; here is equal to the value of first byte


; For second input
tsize2 db 10
asize2 db ?
array2 db 10 dup(?)

; Here both values would be concatenated. So, it must be sum of the sizes of both inputs long
array3 db 20 dup('$')

.code

main:

; Set the value of ds
mov ax, @data
mov ds, ax

; Display first message
mov ah, 09h
mov dx, offset msg1
int 21h

; Get first input
mov ah, 0ah
mov dx, offset tsize1
int 21h

; Display second message
mov ah, 09h
mov dx, offset msg2
int 21h

; Get second input
mov ah, 0ah
mov dx, offset tsize2
int 21h

; Copy first string

; Loop uses cx register
mov cx, 0 ; So clean it up first
mov cl, asize1 ; Put actual size of first input in cl

; cx
; 0 7
; ch cl

; The value of cx is same as value of cl now

mov si, offset array1    ; Offset of first byte of first input is moved in si
mov di, offset array3    ; Offset of first byte of new string is moved in di ; Here the loop starts

start1:

mov al, [si]    ; Copy the value at the address of si in al (one byte)
mov [di], al    ; Copy this value at the address of di from al

inc si          ; Move to next byte of source
inc di          ; Move to next byte of target

loop start1

; loop subtracts 1 from cx and checks if its zero. Zero means break the loop
; So, the loop appears to run cx times which has number of characters in first input

; First string copied, start the second

mov cx, 0
mov cl, asize2

mov si, offset array2  ; Start copying from first byte of second string
                       ; But the target remains the same

start2:

mov al, [si]
mov [di], al
inc si
inc di

loop start2

; Copy complete

; Display message
mov ah, 09h
mov dx, offset msg3
int 21h

; Display the string
mov dx, offset array3
int 21h

; Exit
mov ah, 4ch
int 21h

end main