16 bit word lc-3 BR and JMP instruction - lc3

I have a issue that I cannot seem to figure out, when it comes to 16 bit words. Below i have attached the corresponding code and the the imagine that gives a bit of a description of what I am asking help for.
.orig x3000
ld r1,n
lea r5,data
add r1,r1,#-1
loop:
str r1,r5,0
add r5,r5,r2
add r1,r1,#-1
brp loop
trap x25
data: .blkw 2
n: .fill 10
.end
Here is the image
I get the nzp codes but im not sure how to calculate the pcoffset9 so it would be greatly appreciated if someone could help with this example.

The formula to calculate the PCOffset is as follows
ADDRESS_OF_SYMBOL - (INSTRUCTION_ADDRESS + 1)
.orig x3000
LEA R0, HELLO_WORLD ; x3000
PUTS ; x3001
HALT ; x3002
HELLO_WORLD .stringz "HELLO WORLD" ; x3003
.end
so the PCOffset in the LEA instruction is as follows
ADDRESS_HELLO_WORLD - (ADDRESS_LEA + 1)
x3003 - (x3000 + 1) = 2
When the LEA instruction executes remember that the PC is already incremented as part of Fetch so PC will be at x3001. We add the 2 to it to get x3003 the address of where the label HELLO_WORLD is.

Related

Line drawing algorithm in assembly

I'm trying to create a line-drawing algorithm in assembly (more specifically Bresenham's line algorithm). After trying an implementation of this algorithm, the algorithm fails to work properly even though I almost exactly replicated the plotLineLow() function from this Wikipedia page.
It should be drawing a line between 2 points, but when I test it, it draws points in random places in the window. I really don't know what could be going wrong because debugging in assembly is difficult.
I'm using NASM to convert the program to binary, and I run the binary in QEMU.
[bits 16] ; 16-bit mode
[org 0x7c00] ; memory origin
section .text ; code segmant
global _start ; tells the kernal where to begin the program
_start: ; where to start the program
; main
call cls ; clears the screen
update: ; main loop
mov cx, 0x0101 ; line pos 1
mov bx, 0x0115 ; line pos 2
call line ; draws the line
jmp update ; jumps to the start of the loop
; functions
cls: ; function to clear the screen
mov ah, 0x00 ; set video mode
mov al, 0x03 ; text mode (80x25 16 colours)
int 0x10 ; BIOS interrupt
ret ; returns to where it was called
point: ; function to draw a dot at a certain point (dx)
mov bx, 0x00ff ; clears the bx register and sets color
mov cx, 0x0001 ; clears the cx register and sets print times
mov ah, 0x02 ; set cursor position
int 0x10 ; BIOS interrupt
mov ah, 0x09 ; write character
mov al, ' ' ; character to write
int 0x10 ; BIOS interrupt
ret ; returns to where it was called
line: ; function to draw a line at two points (bx, cx)
push cx ; saves cx for later
push bx ; saves bx for later
sub bh, ch ; gets the value of dx
mov [dx_L], bh ; puts it into dx
sub bl, cl ; gets the value of dy
mov [dy_L], bl ; puts it into dy
mov byte [yi_L], 1 ; puts 1 into yi (positive slope)
cmp byte [dy_L], 0 ; checks if the slope is negative
jl .negative_y ; jumps to the corresponding sub-label
jmp .after_negative_y ; if not, jump to after the if
.negative_y: ; if statement destination
mov byte [yi_L], -1 ; sets yi to -1 (negative slope)
neg byte [dy_L] ; makes dy negative as well
.after_negative_y: ; else statement destination
mov ah, [dy_L] ; moves dy_L into a temporary register
add ah, ah ; multiplies it by 2
sub ah, [dx_L] ; subtracts dx from that
mov [D_L], ah ; moves the value into D
pop bx ; pops bx to take a value off
mov [y_L], bh ; moves the variable into the output
pop cx ; pops the stack back into cx
mov ah, bh ; moves x0 into ah
mov al, ch ; moves x1 into al
.loop_x: ; loop to go through every x iteration
mov dh, ah ; moves the iteration count into dh
mov dl, [y_L] ; moves the y value into dl to be plotted
call point ; calls the point function
cmp byte [D_L], 0 ; compares d to 0
jg .greater_y ; if greater, jumps to the if statement
jmp .else_greater_y ; if less, jumps to the else statement
mov bh, [dy_L] ; moves dy into a temporary register
.greater_y: ; if label
mov bl, [yi_L] ; moves yi into a temporary register
add [y_L], bl ; increments y by the slope
sub bh, [dx_L] ; dy and dx
add bh, bh ; multiplies bh by 2
add [D_L], bh ; adds bh to D
jmp .after_greater_y ; jumps to after the if statement
.else_greater_y: ; else label
add bh, bh ; multiplies bh by 2
add [D_L], bh ; adds bh to D
.after_greater_y: ; after teh if statement
inc ah ; increments the loop variable
cmp ah, al ; checks to see if the loop should end
je .end_loop_x ; if it ended jump to the end of teh loop
jmp .loop_x ; if not, jump back to the start of the loop
.end_loop_x: ; place to send the program when the loop ends
ret ; returns to where it was called
section .data ; data segmant
dx_L: db 0 ; used for drawing lines
dy_L: db 0 ; ^
yi_L: db 0 ; ^
xi_L: db 0 ; ^
D_L: db 0 ; ^
y_L: db 0 ; ^
x_L: db 0 ; ^
section .text ; code segmant
; boot the OS
times 510-($-$$) db 0 ; fills up bootloader space with empty bytess
db 0x55, 0xaa ; defines the bootloader bytes
I see no video mode
just 80x25 text VGA mode (mode = 3) you set at start with cls so how can you render points? You should set the video mode you want assuming VGA or VESA/VBE see the link above.
why to heck use VGA BIOS for point rendering?
that will be slooooooooow and I have no idea what it does when no gfx mode is present. You can render points by direct access to VRAM (at segment A000h) Ideal use 8/16/24/32bit video modes as they have pixels aligned to BYTEs ... my favorite is 320x200x256c (mode = 19) as it fits into 64K segment so no paging is needed and pixels are Bytes.
In case you are using characters instead of pixels then you still can use access to VRAM in the same way just use segment B800h and chars are 16 bit (color and ASCII).
integer DDA is faster then Bresenham on x86 CPUs since 80386
I do not code in NASM for around 2 decades and closest thing to line I found in my archive is this:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
line: pusha ;ax=x0,bx=x1,dl=y0,dh=y1,cl=col
push ax ;expecting ES = A000h
mov si,bx
sub si,ax
sub ah,ah
mov al,dl
mov bx,ax
mov al,dh
sub ax,bx
mov di,ax
mov ax,320
sub dh,dh
mul dx
pop bx
add ax,bx
mov bp,ax
mov ax,1
mov bx,320
cmp si,32768
jb .r0
neg si
neg ax
.r0: cmp di,32768
jb .r1
neg di
neg bx
.r1: cmp si,di
ja .r2
xchg ax,bx
xchg si,di
.r2: mov [.ct],si
.l0: mov [es:bp],cl
add bp,ax
sub dx,di
jnc .r3
add dx,si
add bp,bx
.r3: dec word [.ct]
jnz .l0
popa
ret
.ct: dw 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
So you have something to cross check (took me a while to find it in my archives as I coded whole 3D polygonal engines with textures at that time so I do not have much 2D code in NASM...)
The example expects 320x200x256c VGA video mode
If you were writing a DOS .com file, things would be a bit simpler: segment registers would all be set the same, with your code/data at offset 100 from them. And you could end with ret.
[BITS 16]
[ORG 100h]
[SEGMENT .text]
ret
As #bitRAKE and #PeterCoders pointed out in case You run this in BOOT SECTOR the org is ok. However in such case there is no OS present so if you were going to do more with the stack or any other block of memory outside your 512 bytes, you'd want to point the stack to somewhere known. (It does start out valid, though, because interrupts are enabled.)
More importantly, you need to initialize DS to match your ORG setting, so ds:org reaches a linear address of 7C00. With org 0x7c00, that means you want DS=0. Otherwise instructions like mov [dx_L], bh would be using memory at some unknown location.
[BITS 16]
[ORG 7C00h]
[SEGMENT .text]
mov ax,0000h
mov ds,ax ; DS=0 to match ORG
mov ss,ax ; if you set SS:SP at all, do it back-to-back
mov sp,7C00h ; so an interrupt can't fire half way through.
; here do your stuff
l0:
hlt ; save power
jmp l0
Hope you are using VC or NC configured as IDE for NASM and not compiling/linking manually
This one is usable in MS-DOS so if you are running BOT SECTOR you out of luck. Still You can create a *.com executable debug and once its working in dos change to BOOT SECTOR...
see Is there a way to link object files for DOS from Linux? on how to setup MS-DOS Volkov commander to automatically compile and link your asm source code just by hitting enter on it ... You can also run it just by adding line to the vc.ext line ... but I prefer not to so you can inspect error log first
Convenient debugging
You can try to use MS-DOS (DOSBox) with ancient Borland Turbo C/C++ or Pascal and use their inline asm { .... } code which can be traced and stepped directly in the IDE. However it uses TASM (different syntax to NASM) and have some restrictions ...
Sadly I never saw any decent IDE for asm on x86 platform. The best IDE for asm I worked with was Herkules on ZX Spectrum ... was possible to done things even modern C++ IDEs doesnt have.

Where to learn LC3 with proper full explanations?

This may seem rather silly, but there actually are hardly any resources to learn LC-3. I can't manage to seem to find a proper in depth analysis of the subject and explain how things work, I mean sure, you can find simple definitions and what certain op/pseudo-op codes do, but nothing written in full and fully explained.
If someone could do a full analysis of the following:
; Hello name in LC-3 assembler
.orig x3000
lea r0, what
puts
lea r1, name
; typical assembly language hack coming up
add r1, r1, #-1
char getc
putc
add r2, r0, #-10
brz completed; was a newline
str r0, r1, #0
add r1, r1, #1
brnzp char
completed lea r0, hello
puts
halt
That would probably extremely lengthy, but also very appreciated. (Maybe this is the first stack post for a full analysis of LC-3 code resource?)
p.s I don't expect the person who answers to explain what each op/pseudo op code does but at least be very specific about how the operator performs and does its work
I mostly learned how LC3 worked from plugging in code and stepping through it. Though I would reference Appendix A in the book a LOT.
; Hello name in LC-3 assembler
.orig x3000 ; Starting place in memory for our code
lea r0, what ; Load the memory address of variable what
puts ; Print the string who's memory address is stored in R0
lea r1, name ; Load the memory address of the variable name into R1
; typical assembly language hack coming up
add r1, r1, #-1 ; Subtract 1 from R1, then store into R1
char getc ; Get a single char from the user, store into R0
putc ; Print that same char to the console
add r2, r0, #-10 ; R2 = R0 - 10
brz completed ; If the user presses ENTER (Ascii 10)
; and we've subtracted 10 then we'll get a 0, exit program
str r0, r1, #0 ; Store the value of R0 into memory[R1 + 0]
add r1, r1, #1 ; R1 = R1 + 1
brnzp char ; Jump to Clear no matter what
completed lea r0, hello ; Load the memory address of variable hello into R0
puts ; Print the string stored in hello
halt ; Stop the program

Adding two numbers in base 10 on Assembler

How can I add 2 numbers that their value is on base 16 and make the result on "base 10" on assembler. For example:
"5h+5h=10h" - I know it's wrong, I just want it to be visually 10h
And not:
5h+5h=Ah
CODE:
MOV AX,5h
MOV BX,5h
ADD AX,BX
result: ax=Ah - Not the result that i want...
result: ax=10h - The result that i want.
I tried to figure it out with google but didn't find anything that can help me...
Here is the code you are looking for
MOV AX,5h
MOV BX,5h
ADD AX,BX
DAA
Now AX contains 10h
Ok so i figure it out with #Fifoernik 's help
so the problem is that if i want to do it with 16bit (for example 99h+1h) values i need to do it like this using DAA operan and CF flag
pop ax
pop bx
add al,bl
daa ; dec id values
mov cl,al
mov al,ah
jc Carry; do i have carry
add al,bh
daa ; do the magic thing
JMP finito
Carry:
add al,1 ; add the carring...
add al,bh
daa ;can some one tell me what exactly daa does?
finito:
mov ch,al
push cx
ret
daa working only on AL so you'ill need to use the carry flag to add the carry like:
AH AL
1 <----- carry
00 99 <-- DAA take care of the 99 and make it 0 when its A0h
00 01+
-- --
01 00 ---> result 100h

NASM jmp wonkiness

I'm writing a Forth inner interpreter and getting stuck at what should be the simplest bit. Using NASM on Mac (macho)
msg db "k thx bye",0xA ; string with carriage return
len equ $ - msg ; string length in bytes
xt_test:
dw xt_bye ; <- SI Starts Here
dw 0
db 3,'bye'
xt_bye dw $+2 ; <- Should point to...
push dword len ; <-- code here
push dword msg ; <--- but it never gets here
push dword 1
mov eax, 0x4 ; print the msg
int 80h
add esp, 12
push dword 0
mov eax, 0x1 ; exit(0)
int 80h
_main:
mov si,xt_test ; si points to the first xt
lodsw ; ax now points to the CFA of the first word, si to the next word
mov di,ax
jmp [di] ; jmp to address in CFA (Here's the segfault)
I get Segmentation Fault: 11 when it runs. As a test, I can change _main to
_main:
mov di,xt_bye+2
jmp di
and it works
EDIT - Here's the simplest possible form of what I'm trying to do, since I think there are a few red herrings up there :)
a dw b
b dw c
c jmp _my_actual_code
_main:
mov si,a
lodsw
mov di,ax
jmp [di]
EDIT - After hexdumping the binary, I can see that the value in b above is actually 0x1000 higher than the address where label c is compiled. c is at 0x00000f43, but b contains 0x1f40
First, it looks extremely dangerous to use the 'si' and 'di' 16-bit registers on a modern x86 machine which is at least 32-bit.
Try using 'esi' and 'edi'. You might be lucky to avoid some of the crashes when 'xt_bye' is not larger than 2^16.
The other thing: there is no 'RET' at the end of xt_bye.
One more: see this linked question Help with Assembly. Segmentation fault when compiling samples on Mac OS X
Looks like you're changing the ESP register to much and it becomes unaligned by 16 bytes. Thus the crash.
One more: the
jmp [di]
may not load the correct address because the DS/ES regs are not used thus the 0x1000 offset.

How do I print 0,2,4,6,... in assembly language?

I have an assignment from my comp. system org. subject and unfortunately I'm kind of new when it comes to assembly language. I'm supposed to write a program that displays the numbers 0,2,4,6,8,10 respectively. How would I go about this?
Maybe this'll answer my question: (Reactions please)
.model small
.stack 100H
.data
.code
call proc
mov cx,5
mov dx,0
L1:
mov bx,2
add dx,bx
mov ah,02h
loop L1
int 21
endp
Go see your lecturer and/or tutor and ask for advice. That's what they're there for. You haven't given us anywhere near enough info to help you out.
Here's what I think your ABCD program should look like. I suggest you use it as a baseline to try to make a 0 2 4 ... version.
model proc
.stack 100H
.data
.call
main proc
mov cx,10 ; 10 loops only.
mov dx,40h ; start dx at 'A' - 1.
L1:
inc dx ; move to next character.
mov ah,02h ; int 21,02 is print character.
int 21h
loop L1 ; loop until cx is 0
mov ax,4c00h ; int 21,4c is exit with al holding exit code.
int 21
endp
When you've at least had a go at converting this, post the code and we'll critique what you've done.
If you're taught something, it never lasts but, if you learn something, it lasts forever (alcohol-addled braincells notwithstanding :-).
Int 21 is the DOS interrupt which allows assembler programs to use various DOS functions. It's conceptually a huge switch statement based on the AH register which is why you'll see things like Int 21 Fn 02, which means execute mov ah,2 followed by int 21.
Int 21 Fn 02 will take the contents of DL and output that to the screen. So the sequence:
mov ah,02h
mov dl,41h
int 21h
will output the 'A' character (0x41).
Similarly, Int 21 Fn 4c will exit the current running process.
I'm sure your class gave you some education here.
Can you code enough assembly to print one or two numbers?
Can you code enough to calculate the numbers, even if you can't print them?
Post that much code, and you may find help here.
Otherwise, you're asking others to actually do your homework for you.
Assembly language is a symbolic representation of the numeric machine codes and other constants needed to program a particular CPU (or architecture). So assembly language for Macs (most recently Intel's X86) is different from that used to on the iPhone - ARM.
Your teacher is also probably expecting you to realise the difference between the binary form of the number you will count with, and the ASCII format you will use to display to the screen.
You do know there is more than one flavor of "Assembly Language."
You can do it exactly like the program which prints A, B, C, D, etc.: except that instead of starting at 'A', start at '0; and instead of increasing by 1 each time (from 'A' to 'B'), increase by 2 (from '0' to '2').
After printing '0', '2', '4', '6', and '8', the next number that you want to print is '10'.
To print '10', you can print '1' followed by '0'. Or, instead of invoking int 21 with ah=2 (which prints one character at a time), you can set ah=9 to print a string (set ds:dx to a block of memory which contains "10$").
Later you suggested the following solution and asked for criticism:
.model small
.stack 100H
.data
.code
main proc
call defineuser1
call defineuser2
mov cx,5
userdefine1 proc
L1:
mov dx,0
mov bx,2
add dx,bx
mov ah,02h
loop L1
int 21h
endp
userdefine2 proc
mov ah, 4ch
int 21h
userdefine2
endp
My criticisms are as follows:
defineuser1 doesn't exist (I think you mean userdefine1)
setting cx needs to be inside (not before) the procedure
invoking int 21 needs to be inside (not outside) the loop
you need special handling for "10" as I mentioned above
There's a difference between '0' (the ASCII character/digit) and 0 (the number) ... you need to print the character/digit, not the number
You need to learn to test your code (write it, step through it with debugger, and debug it), preferably before you post questions about it.
You would have a counter beginning at zero and repeatedly increment it by two, printing the result.
.model small
.stack 100H
.code
.data
var2 DB "10$"
main proc
mov cx,4
mov ax,0
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov ax,0
var1:
add ax,2
mov dl,al
add dl,30h
mov bx,ax
mov ah,2h
int 21h
mov ax,bx
loop var1
mov ax,#data
mov ds,ax
mov dx,offset var2
mov ah,09h
int 21h
main endp
end main
I'm new in computer science and when i saw this question i just wanted to try it. I have managed in doing it and here is the code
MOV AX, 0
MOV BX, 2
ADDLOOP:
ADD AX, BX
CMP AX, 10
JE DONE
JMP ADDLOOP
DONE:
Ok. That's my best attempt. Lots of details left out. I should also mention that I have no frigging clue how to print a char to the screen.
Like others have mentioned, you didn't specify which assembly language so I chose x86.
Finally, go talk to your instructors, they'll help you much more than we can.
Are you using a macro for the output?
should be something like...
mov eax, 0
myloop: cmp eax, 10
jg done
output macro eax
add eax, 2
jmp myloop
done:
of course that's for 8086 assembly.

Resources