ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
º Adam's Assembler Tutorial 1.0 ÇÄ¿ º º ³
º PART VI º ³ ÈÍÑÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ ³
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
Contact : blackcat@faroc.com.au http://www.faroc.com.au/~blackcat
Note : Adam's Assembler Tutorial is COPYRIGHT, and all rights are
reserved by the author. You may freely redistribute only the ORIGINAL archive, and the tutorials should not be edited in any
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Hello again, Assembler coders. This edition is a little late, but I hada lot of other things to finish, and I'm working on a game of my own now.
It's a strategy game, like Warlords II, and I think I'm going to have towrite most of the code for it in 640x480, not my beloved 320x200 - but I may
change my mind. Heck, the amount of games I started writing but never gotaround to finishing is pretty large, so this one may not get all that far.
Anyway, I said we'd be having a look at some line/circle routines this week,
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Last week we came up with the following horizontal line routine -
mov ax, 0A000h mov es, ax ; Point ES to the VGA
sub cx, ax ; CX = Difference of X2 and X1
shl dx, 6 ; Y SHL 6 add dx, bx ; DX = Y SHL 8 + Y SHL 6
mov al, color ; Put the color to plot in AL rep stosb ; Draw the line
Now although this routine was much faster than the BGI routines, (or whateveryour compiler provides), it could be improved upon greatly. If we go through
the routine with the list of clock ticks provided in the last tutorial, you'll
I'll leave optimization up to you for now, (we'll get onto that in a latertutorial), but either replacing the STOSB with MOV ES:[DI], AL or STOSW will
speed things up quite a bit. Don't forget that if you decide to use a loop,to whack words onto the VGA, you will have to decrement CX by one.
Now, lets get on to a vertical line. We'll have to calculate the offset of
the first pixel as we did with the horizontal line routine, so something likethis should do:
mov ax, 0A000h ; Put the VGA segment into AX mov es, ax ; Point ES to the VGA
mov ax, Y1 ; Move the first Y value into AX
shl ax, 6 ; Y x 2 to the power 6 mov di, ax ; Move the new Y value into DI
shl ax, 2 ; Now we have Y = Y x 320 add di, ax ; Add that value onto DI
mov cx, Y2 ; Store Y2 in CX mov al, Color ; Store the color to plot with in AL
sub cx, Y1 ; CX = vertical length of line
Plot: mov es:[di], al ; Put the a pixel at the current offset
add di, 320 ; Move down to the next row dec cx ; Decrement CX by one
jnz Plot ; If CX <> 0, then keep on plotting
Not a fantastic routine, but it's pretty good all the same. Note how it was
possible to perform a comparison after DEC CX. This is an extremely usefulconcept, so don't forget that it is possible.
Have a play around with the code, and try and speed it up. Try other methods
of finding the offset, or different methods of flow control.
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Now, that was the easy stuff. We are now going to have a look at a lineroutine capable of drawing diagonal lines.
The following routine was picked up from SWAG, author unknown, and is an
ideal routine to demonstrate a line algorithm. It is in great need ofoptimization, so this can be a task for you - if you wish. Some of the
1) Whoever wrote it had never heard of XCHG - this would save quite a few clock ticks;
2) It makes one of the great sins of unoptimized code - it will say, move a value to AX, and then perform an operation involving AX in the next
instruction, thus incurring a penalty cycle. (We'll talk about this next week).
3) It works with BYTES not WORDS, so the speed of writing to the VGA could
4) And the biggest sin of all, it uses a MUL to find the offset. Try using shifts or an exchange to speed things up.
Anyway, I put the comments in, and I feel that it is fairly self explanatory
as it is, so I won't go over how it works. You should be able to pick thatup for yourself. Work through the routine, and see how the gradient for the
Procedure Line(X1, Y1, X2, Y2 : Word; Color : Byte); Assembler;
Asm { Line } mov ax, [X2] { Move X2 into AX }
sub ax, [X1] { Get the horiz length of the line - X2 - X1 } jnc @Dont1 { Did X2 - X1 yield a negative result? }
neg ax { Yes, so make the horiz length positive }
@Dont1: mov [DeX], ax { Now, move the horiz length of line into DeX }
mov ax, [Y2] { Move Y2 into AX } sub ax, [Y1] { Subtract Y1 from Y2, giving the vert length }
jnc @Dont2 { Was it negative? } neg ax { Yes, so make it positive }
mov [DeY], ax { Move the vert length into DeY } cmp ax, [DeX] { Compare the vert length to horiz length }
jbe @OtherLine { If vert was <= horiz length then jump }
mov ax, [Y1] { Move Y1 into AX } cmp ax, [Y2] { Compare Y1 to Y2 }
jbe @DontSwap1 { If Y1 <= Y2 then jump, else. } mov bx, [Y2] { Put Y2 in BX }
mov [Y1], bx { Put Y2 in Y1 } mov [Y2], ax { Move Y1 into Y2 }
{ So after all that. } { Y1 = Y2 and Y2 = Y1 }
mov bx, [X2] { Put X2 into BX } mov [X1], bx { Put X2 into X1 }
@DontSwap1: mov [IncF], 1 { Put 1 in IncF, ie, plot another pixel }
mov ax, [X1] { Put X1 into AX } cmp ax, [X2] { Compare X1 with X2 }
jbe @SkipNegate1 { If X1 <= X2 then jump, else. } neg [IncF] { Negate IncF }
@SkipNegate1: mov ax, [Y1] { Move Y1 into AX }
mov bx, 320 { Move 320 into BX } mul bx { Multiply 320 by Y1 }
mov di, ax { Put the result into DI } add di, [X1] { Add X1 to DI, and tada - offset in DI }
mov bx, [DeY] { Put DeY in BX } mov cx, bx { Put DeY in CX }
mov ax, 0A000h { Put the segment to plot in, in AX } mov es, ax { ES points to the VGA }
mov dl, [Color] { Put the color to use in DL } mov si, [DeX] { Point SI to DeX }
mov es:[di], dl { Put the color to plot with, DL, at ES:DI } add di, 320 { Add 320 to DI, ie, next line down }
sub bx, si { Subtract DeX from BX, DeY } jnc @GoOn1 { Did it yield a negative result? }
add bx, [DeY] { Yes, so add DeY to BX } add di, [IncF] { Add the amount to increment by to DI }
loop @DrawLoop1 { No negative result, so plot another pixel } jmp @ExitLine { We're all done, so outta here! }
mov ax, [X1] { Move X1 into AX } cmp ax, [X2] { Compare X1 to X2 }
jbe @DontSwap2 { Was X1 <= X2 ? } mov bx, [X2] { No, so move X2 into BX }
mov [X1], bx { Move X2 into X1 } mov [X2], ax { Move X1 into X2 }
mov ax, [Y1] { Move Y1 into AX } mov bx, [Y2] { Move Y2 into BX }
mov [Y1], bx { Move Y2 into Y1 } mov [Y2], ax { Move Y1 into Y2 }
mov [IncF], 320 { Move 320 into IncF, ie, next pixel is on next row } mov ax, [Y1] { Move Y1 into AX }
cmp ax, [Y2] { Compare Y1 to Y2 } jbe @SkipNegate2 { Was Y1 <= Y2 ? }
@SkipNegate2: mov ax, [Y1] { Move Y1 into AX }
mov bx, 320 { Move 320 into BX } mul bx { Multiply AX by 320 }
mov di, ax { Move the result into DI } add di, [X1] { Add X1 to DI, giving us the offset }
mov bx, [DeX] { Move DeX into BX } mov cx, bx { Move BX into CX }
mov ax, 0A000h { Move the address of the VGA into AX } mov es, ax { Point ES to the VGA }
mov dl, [Color] { Move the color to plot with in DL } mov si, [DeY] { Move DeY into SI }
mov es:[di], dl { Put the byte in DL at ES:DI } inc di { Increment DI by one, the next pixel }
sub bx, si { Subtract SI from BX } jnc @GoOn2 { Did it yield a negative result? }
@GoOn2: loop @DrawLoop2 { Keep on plottin' }
I don't think I made any mistakes with the commenting, but I am pretty tired,and I haven't handy any caffeine for days - let alone hours, so if you spot a
I was going to include a Circle algorithm, but I couldn't get mine to workin Assembler - all the floating point math might have something to do with
it. I could include one written in a high level language, but this is meantto be an Assembler tutorial, not a graphics one. However, if enough people
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
³ ³ ³ THE INS AND OUTS OF IN AND OUT ³
³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
IN and OUT are a very important part of Assembler coding. They allow you to
directly send/receive data from any of the PC's 65,536 hardware ports, orregisters. The basic syntax is as follows:
þ IN <ACCUMULATOR>, <PORT> - Name: Input from I/O port Type: 8086+
value from one of the 65536 hardware ports into the specified accumulator.
ports, and DX is commonly used to identify the port.
þ OUT <PORT>, <ACCUMULATOR> - Name: Output to Port
Description: This instruction outputs the value in the accumulator to <PORT>. Using
the DX register to pass the port to OUT, you may access up to 65,536 ports.
Okay, that wasn't very helpful, as it didn't tell you much about how to useit - let alone what to use it for. Well, if you intend to work with the VGA
much, you'll have to be able to program its internal registers. Similar tothe registers that you've been working with up until now, you can think of
changing them just like interrupts, except: 1) You pass the value to theport, and that's it; and 2) It is pretty near instantaneous.
As an example, we'll cover how to set and get the palette by directly
Now, the VGA has a lot of registers, but the next three you'd better get to
þ 03C7h - PEL Address Register (Read) Sets the palette in read mode
þ 03C9h - PEL Data Register (Read/Write) Read in, or write 3 RGB values, every 3rd write, the
index, or color you are setting, is incremented by one.
If we were to set a color's RGB value, we'd send the value of the color wewanted to change to 03C8h, then read in 3 values from 03C9h. In Assembler,
mov dx, 03C8h ; Put the DAC read register in DX mov al, [Color] ; Put the color's value in AL
out dx, al ; Send AL to port DX inc dx ; Now use port 03C9h
mov al, [R] ; Put the new RED value in AL out dx, al ; Send AL to port DX
mov al, [G] ; Put the new GREEN value in AL out dx, al ; Send AL to port DX
mov al, [B] ; Put the new BLUE value in AL out dx, al ; Send AL to port DX
And that would do things nicely. To read the palette, we'd do this:
mov dx, 03C7h ; Put the DAC write register in DX
mov al, [Color] ; Put the color's value in AL out dx, al ; Send AL to port DX
in al, dx ; Put the value got from port DX in AL les di, [R] ; Point DI to the R variable - this came from Pascal
in al, dx ; Put the value got from port DX in AL les di, [G] ; Point DI to the G variable
in al, dx ; Put the value got from port DX in AL les di, [B] ; Point DI to the B variable
Note how that routine was coded differently. This was originally a Pascal
routine, and as Pascal doesn't like you messing with Pascal variables inAssembler, you have to improvise.
If you are working in stand alone Assembler, then you can code this much more
efficiently, like the first example. I left the code as it was so those whoare working with a high-level language can get around a particularly annoying
Now you have seen how useful IN and OUT can be. Directly controlling hardwareis both fast and efficient. In the next few weeks, I may include a list of
some of the most common ports, but if you have a copy of Ralf Brown'sInterrupt List, (available at X2FTP), you will already have a copy.
Note: You can find a link to Ralf's Interrupt List on my homepage.
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Now, although we have been using the flags register in almost all our code
up until this point, I haven't really gone into depth about it. You can workblissfully unaware of the flags, and compare things without knowing what's
really happening, but if you want to get further into Assembler, you'llneed to know some more.
Back in Tutorial Three, I gave an extremely simplistic view of the FLAGS
register. In reality, the FLAGS, or EFLAGS register is actually a 32-bitregister, although only bits 0-18 are used. We really don't need to know any
of the flags above bit 11 for now, but it's good to know they are there.
The EFLAGS register actually looks like this:
18 17 16 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
AC VM RF -- NT IO/PL OF DF IF TF SF ZF -- AF -- PF -- CF
þ VM - Virtual 8086 Mode þ RF - Resume Flag
þ NT - Nested Task Flag þ IOPL - I/O Privilege Level - has a value of 0,1,2 or 3 thus 2 bits big
This bit is set to ONE if an arithmetic instruction generated a result that was too large or too small to fit in the destination
þ DF - Direction Flag When set to ZERO, string instructions, such as MOVS, LODS and
STOS will increment the memory address they are working on by one. This means that say, DI, will be incremented when you use STOSB
to put a pixel at ES:DI. Setting the bit to ZERO will decrement the memory address after each call.
þ IF - Interrupt Enable Flag When this bit is set, the processor will respond to external
hardware interrupts. When the bit is reset, hardware interrupts are ignored.
When this bit is set, an interrupt will occur immediately after the next instruction executes. This is generally used in debugging.
This bit is changed after arithmetic instructions. The bit receives the high-order bit of the result, and if set to ONE,
it indicates that the result of the operation was negative.
þ ZF - Zero Flag This bit is set when arithmetic instructions generate a result of
þ AF - Auxiliary Carry Flag This bit indicates that a carry out of the low-order nibble of AL
occurred in an arithmetic instruction.
þ PF - Parity Flag This bit is set to one when an arithmetic instruction results in
þ CF - Carry Flag This bit is set when the result of an arithmetic operation is
too large or too small for the destination register or memory address.
Now, of all those above, you really won't have to worry too much about mostof them. For now, just knowing CF, PF, ZF, SF, IF, DF and OF will be
sufficient. I didn't give the first few comments as they are fairlytechnical, and are used mostly in protected mode and complex situations. You
You can, if you wish, move a copy of the flags into AH with LAHF - (Load AHwith Flags) - and modify or read individual bits, or change the status of
bits more easily with CLx and STx. However you plan to change the flags,remember that they can be extremely useful in many situations.
(They can also be very annoying when late at night, lines start drawing
backwards, and you spend an hour wondering why - then remember that youforgot to clear the direction flag!)
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
I think we've covered quite a few important topics in this tutorial. Brush
up on the flags, and go over the largish line routine, as it is an excellentexample of flow control. Make sure your skills at controlling instruction
Next week I'll try to tie all the topics we've covered over the last fewweeks together, and present some form of review of all that you've learnt.
Next week I'll also go into optimization, and how you can speed up all thecode we've worked with so far.
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
þ A review of all you've learnt þ Optimization
þ Declaring procedures in Assembler þ Linking your code to C/C++ or Pascal
If you wish to see a topic discussed in a future tutorial, then mail me, and
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Don't miss out!!! Download next week's tutorial from my homepage at:
STUDENT´S NAME: _________________________________________________1. COMPLETE THE FOLLOWING SENTENCES USING THE WORDS LISTED. MODIFY WHENa) Who do you generally _____________________ your kids with when you travel on business?b) I generally leave home at 7:20 _____________________ that I can get to work on time. c) Every time I need help, I _____________________ my best friends. d) As a marke
REPORT ATTIVITA’ TURISMO BERGAMO Secondo Trimestre 2012 APRILE The Key To Bergamo Aprile – Maggio 2012 E’ uscita ed è in distribuzione la rivista trimestrale di Turismo Bergamo: The Key to Bergamo. La guida, nella sua nuova veste grafica, è distribuita in 50 mila copie in: camere e hall di Hotel, Bed & Breakfast, Residence, Ostelli e Case Vacanza di Bergamo