LC-3 Instruction Understanding - lc3

Hey Stackoverflow I was going over some example code our professor has left us to study with, and I've had some problems understanding what some of the codes mean and it doesnt help that some of his comments are vague. the first is R3,R1,0; check for end of line, the second thing I dont get is really the logic behind the beginning of the placeolnul, and lastly the .fill values for negeol which seem oddly specific but i have no idea why. if you could really help me with those issues it would really help me understand the code alot better.
.orig x3000
getstring:
lea r0,prompt ;get string prompt
puts
ld r1,negeol ;to test end of line
lea r2,rdbuff ;prep to read string
rdloop:
getc ;get string char
out
str r0,r2,0 ;store it
add r3,r1,r0 ;check for end of line
brz placeeolnul ;if so process
add r2,r2,1 ;ready for next char
br rdloop ;get it
placeeolnul:
and r0,r0,0 ;overwrite eol with
str r0,r2,0 ;nul
lea r1,rdbuff ;get address for len
jsr strlen ;get length
add r0,r0,0 ;if 0
brz quit ;then prog finished
trap xfc ;print length
lea r0,colon ;print colon
puts
lea r0,eol ;print lf
puts
br getstring ;go again
quit
halt
prompt: .stringz "Enter a string:"
eol: .fill x000d ; or x000a
.fill x0000
negeol: .fill xfff3 ; or xfff6
colon: .fill x003a
rdbuff .blkw 80
; length subroutine
strlen:
and r0,r0,0 ;counter for length
st r2,saveX2 ;save regs used
st r3,saveX3
add r2,r1,0 ;copy of string addr
cloop:
ldr r3,r2,0 ;get char
brz exit ;check for nul
add r0,r0,1 ;incr counter
add r2,r2,1 ;go to next char
br cloop ;process it
exit:
ld r2,saveX2 ;restore used regs
ld r3,saveX3
ret
saveX2: .blkw 1
saveX3: .blkw 1
.end

In regards to this:
add r3,r1,r0 ;check for end of line
Here he is setting up for the subsequent "brz" to branch on zero.
So we are adding the contents of registers r1 and r0 and putting them in r3, so that if r3 is zero, we will branch in the next line:
brz placeeolnul ;if so process
Remember that r1 is already pre-loaded with a value based on this code:
ld r1,negeol ;to test end of line
which has loaded r1 with the value at the tag negeol per this code:
negeol
.fill xfff3 ; or xfff6
So this is just a quick way to detect if an EOL character has been found by adding the compliment of the EOL character, which presumably here is xfff3.
Regarding the brz statement and the logic for it (your 2nd question):
brz placeeolnul ;if so process
As mentioned, we are branching here if the add is zero. If we do not branch (meaning we did not find a EOL character), then we will continue, but that continuation will essentially loop back to rdloop tag:
add r2,r2,#1 ;ready for next char
br rdloop ;get it
If we DO jump via the brz line, that means we got the entire string, and we are ready to process it.... so jumping to placeeolnul just puts us into the code where we can process the string:
and r0,r0,#0 ;overwrite eol with
str r0,r2,#0 ;nul
.... etc...
Not sure if you have questions with this part of the code as well.
Hope this helps.
Jeff

Related

How to properly use PUSH and POP

I have a code that has 3 procedures, one to get an input from the user, one to display a multiplication result, and lastly one for an error message. I am trying to implement the PUSH and POP operations and get my code in to the stack. It will seem long but it makes sense to me, here it is...
.data
line BYTE "The answer is ",0
line2 BYTE "Enter a number",0
kline3 BYTE Wrong.",0
int SWORD ?
.code
Val PROC
call ReadInt
mov int,edx
cmp int, 10000
jl 1
jmp end
L1: cmp intVal1, -10000
jg 2
call error
jmp end
2: ret
Val ENDP
main PROC
call Val
call Val
imul val, val
exit
main ENDP
END main
All this simply does it call to get 2 inputs twice and then call to display the the multiplied result. My question is how do you implement push and pop in to here to have it all make sense?
I would assume that you need to push in the GetValue procedure to put in input in to the stack and then pop it after each call in the main procedure and maybe do the same with the display procedure?
I am just struggling to figure it out so any help would be great!
PS. This code is an asm file in visual studio 2010
Your first call to GetValue stores its result in intVal. But then your second call to GetValue also stores its result in intVal, so the first result is forever lost.
Your MultiplyAndDisplay function expects one of the operands in intVal, and the other operand in eax. So, what you need to do is push [intVal] after the first call to GetValue, and pop eax after the second call to GetValue.
Note that the square brackets in push [intVal] are in some notation that actually makes sense, but if I remember correctly the microsoft assembler does not support that notation which actually makes sense, so you might have to code push intVal instead, or push dword ptr intVal, or something nonsensical like that to get it to work.
Because your question is tagged MASM, this is a MASM answer:
Your code can be restructured in a way that uses the MASM directive PROC with parameters and the INVOKE directive for parameter passing:
MultiplyAndDisplay PROC val1: SDWORD, val2: SDWORD
mov eax, val1
imul eax, val2 ; signed multiply of val1 by val2
mov edx, OFFSET prompt
call WriteString ; Writes the prompt in edx
call WriteDec ; Writes the value in eax
ret
MultiplyAndDisplay ENDP
main PROC
call GetValue
push [intVal] ; PUSH firstParam to the stack
call GetValue
pop eax ; POP previous param/intVal to EAX
invoke MultiplyAndDisplay, eax, intVal ; MultiplyAndDisplay, firstParam(EAX), secondParam(intVal)
exit
main ENDP

Piping into SET /P fails due to uninitialised data pointer?

Supposing we have got a text file sample.txt:
one
two
...
Now we want to remove the first line:
two
...
A quick way to do that is to use input redirection, set /P and findstr1 (I know there are other ways using more or for /F, but let us forget about them for now):
#echo off
< "sample.txt" (
set /P =""
findstr "^"
)
The output is going to be as expected.
However, why is the output empty when I replace the input redirection < by type and a pipe | :
#echo off
type "sample.txt" | (
set /P =""
findstr "^"
)
When I replace set /P ="" by pause > nul, the output is what I expect -- the input file is output but with the first character of the first line missing (as it is consumed by pause). But why does set /P seem to consume everything instead of only the first line like it does with the redirection < approach? Is that a bug?
To me it looks like set /P fails to adequately initialise the reading pointer to the piped data.
I watched that strange behaviour on Windows 7 and on Windows 10.
It becomes even more weird: when calling the script containing the pipe multiple times, for instance by a loop like for /L %I in (1,1,1000) do #pipe.bat, and the input file contains about fifteen lines or more, sometimes (a few times out of thousand) a fragment of the input file is returned; that fragment is exactly the same each time; it seems that there are always 80 bytes missing at the beginning.
1) findstr hangs in case the last line is not terminated by a line-break, so let us assume such is there.
When retrieving data, the set /p tries to fill a 1023 character buffer (if they are available) with data from stdin. Once this read operation has ended, the first end of line is searched and once it has been found (or the end of the buffer has been reached), the SetFilePointer API is called to reposition the input stream pointer after the end of the read line. This way the next read operation will start to retreive data after the read line.
This works flawlessly when a disk file is associated with the input stream, but as Microsoft states in the SetFilePointer documentation
The hFile parameter must refer to a file stored on a seeking device;
for example, a disk volume. Calling the SetFilePointer function with a
handle to a non-seeking device such as a pipe or a communications
device is not supported, even though the SetFilePointer function may
not return an error. The behavior of the SetFilePointer function in
this case is undefined.
What is happening is that, while not generating any error, the call to reposition the read pointer fails when stdin is associated with a pipe, the pointer is not moved back and the 1023 bytes (or the number of available read bytes) keep read.
edited in response to Aacini request
The set command is processed by the eSet function, who calls SetWork to determine which type of set command will be executed.
As it is a set /p the SetPromptUser function is called and from this function the ReadBufFromInput function is called
add esp, 0Ch
lea eax, [ebp+var_80C]
push eax ; int
push 3FFh ; int
lea eax, [ebp+Value]
push eax ; int
xor esi, esi
push 0FFFFFFF6h ; nStdHandle
mov word ptr [ebp+Value], si
call edi ; GetStdHandle(x) ; GetStdHandle(x)
push eax ; hFile
call _ReadBufFromInput#16 ; ReadBufFromInput(x,x,x,x)
it requests 3FFh (1023) characters from standard input handle (0FFFFFFF6h = -10 = STD_INPUT_HANDLE)
ReadBufFromInput uses the GetFileType API to determine if it should read from the console or from a file
; Attributes: bp-based frame
; int __stdcall ReadBufFromInput(HANDLE hFile, int, int, int)
_ReadBufFromInput#16 proc near
hFile= dword ptr 8
; FUNCTION CHUNK AT .text:4AD10D3D SIZE 00000006 BYTES
mov edi, edi
push ebp
mov ebp, esp
push [ebp+hFile] ; hFile
call ds:__imp__GetFileType#4 ; GetFileType(x)
and eax, 0FFFF7FFFh
cmp eax, 2
jz loc_4AD10D3D
and, as in this case it is a pipe (GetFileType returns 3) the code jumps to the ReadBufFromFile function
; Attributes: bp-based frame
; int __stdcall ReadBufFromFile(HANDLE hFile, LPWSTR lpWideCharStr, DWORD cchWideChar, LPDWORD lpNumberOfBytesRead)
_ReadBufFromFile#16 proc near
var_C= dword ptr -0Ch
cchMultiByte= dword ptr -8
NumberOfBytesRead= dword ptr -4
hFile= dword ptr 8
lpWideCharStr= dword ptr 0Ch
cchWideChar= dword ptr 10h
lpNumberOfBytesRead= dword ptr 14h
This function will call the ReadFile API function to retrive the indicated number of characters
push ebx ; lpOverlapped
push [ebp+lpNumberOfBytesRead] ; lpNumberOfBytesRead
mov [ebp+var_C], eax
push [ebp+cchWideChar] ; nNumberOfBytesToRead
push edi ; lpBuffer
push [ebp+hFile] ; hFile
call ds:__imp__ReadFile#20 ; ReadFile(x,x,x,x,x)
The returned buffer is iterated in search of an end of line, and once it is found, the pointer in the input stream is moved after the found poisition
.text:4AD06A15 loc_4AD06A15:
.text:4AD06A15 cmp [ebp+NumberOfBytesRead], 3
.text:4AD06A19 jl short loc_4AD06A2D
.text:4AD06A1B mov al, [esi]
.text:4AD06A1D cmp al, 0Ah
.text:4AD06A1F jz loc_4AD06BCF
.text:4AD06A25
.text:4AD06A25 loc_4AD06A25:
.text:4AD06A25 cmp al, 0Dh
.text:4AD06A27 jz loc_4AD06D14
.text:4AD06A2D
.text:4AD06A2D loc_4AD06A2D:
.text:4AD06A2D movzx eax, byte ptr [esi]
.text:4AD06A30 cmp byte ptr _DbcsLeadCharTable[eax], bl
.text:4AD06A36 jnz loc_4AD12018
.text:4AD06A3C dec [ebp+NumberOfBytesRead]
.text:4AD06A3F inc esi
.text:4AD06A40
.text:4AD06A40 loc_4AD06A40:
.text:4AD06A40 cmp [ebp+NumberOfBytesRead], ebx
.text:4AD06A43 jg short loc_4AD06A15
.text:4AD06BCF loc_4AD06BCF:
.text:4AD06BCF cmp byte ptr [esi+1], 0Dh
.text:4AD06BD3 jnz loc_4AD06A25
.text:4AD06BD9 jmp loc_4AD06D1E
.text:4AD06D14 loc_4AD06D14:
.text:4AD06D14 cmp byte ptr [esi+1], 0Ah
.text:4AD06D18 jnz loc_4AD06A2D
.text:4AD06D1E
.text:4AD06D1E loc_4AD06D1E:
.text:4AD06D1E mov eax, [ebp+var_C]
.text:4AD06D21 mov [esi+2], bl
.text:4AD06D24 sub esi, edi
.text:4AD06D26 inc esi
.text:4AD06D27 inc esi
.text:4AD06D28 push ebx ; dwMoveMethod
.text:4AD06D29 push ebx ; lpDistanceToMoveHigh
.text:4AD06D2A mov [ebp+cchMultiByte], esi
.text:4AD06D2D add esi, eax
.text:4AD06D2F push esi ; lDistanceToMove
.text:4AD06D30 push [ebp+hFile] ; hFile
.text:4AD06D33 call ds:__imp__SetFilePointer#16 ; SetFilePointer(x,x,x,x)
The short summarize of a long discussion at dostips (mentioned already by Aacini
set /p problems with pipes).
Reading with set /p from a redirect, reads always to the end of the line and removes the \r\n characters.
Reading with set /p from a pipe, reads up to 1023 bytes from the pipe buffer.
It doesn't stop at any \r or \n characters but it drops all content after a \n.
After closing the pipe on the left side, a set /p on the right side will read empty lines.

How to index malloc'd buffer and output

See full code here.
I have filled a buffer (malloc'd) with an fread call and it is a success. I am now trying to iterate over the buffer and commence parsing the input. I'm trying to start really simple by walking the buffer and output each char to the screen. But my loop is just outputting the entire input. Here is the loop portion of the code:
mov ecx, 0
mov ebx, buffer
.readByte:
push DWORD [ebx + 1 * ecx]
push DWORD ecx
push DWORD char
call _printf
add esp, 12
incr ecx
cmp ecx, [fsz]
jge .endRead
jmp .readByte
The contents of the source file that is read in (s1.txt) is:
1 + 2;
My goal is to simply output:
1
+
2
;
Since you used %s format, which indicates a string, and that without a length specifier, why did you expect it to print just a single character? You should try %c format and something like movzx eax, byte [ebx + ecx]; push eax to pass the argument. A %.1s format specifier could also work and then you can keep your argument passing. Don't forget to add a newline too, if you want that. You could also just use putchar of course.
Oh, and ecx is a caller-saved register, as such any function you call may destroy its value. So if you want to keep using that, you need to save and restore it yourself.

8086 ALP String Display

I have written a code for 8086 microprocessor for taking string from keyboard and displaying it as follows
Title Get the string from keyboard and display it
.model small
.stack 100h
.data
str1 db 'Enter String ','$'
str2 db 50 dup('$')
str3 db 0dh, 0ah, '$'
.code
main proc
mov ax,#data
mov ds,ax
mov ah,09h ; for displaying Enter String
lea dx,str1
int 21h
mov ah,0ah ; for taking i/p from keyboard
lea dx,str2
int 21h
mov ah,09h ; for displaying in new line
lea dx,str3
int 21h
mov ah,09h ; for displaying what you have entered
lea dx,str2+2
int 21h
int 21h
mov ah,4ch
int 21h
main endp
end main
I don't understand why we have to give effective address of the string as str+2 to print the inputted string back ? If simply lea dx, str2 is used no string is displayed.
Thanks in advance.
The first byte at STR2 should contain the maximum number of characters to read. The second byte should contain the number of characters already present. Because you initialized STR2 with 50 '$' characters and the ASCII value of '$' is 36 you effectively asked DOS to allow an input of 36 characters that are already there!
Better code str2 db 50,0,50 dup (0)
At the conclusion of this 'Buffered STDIN Input' the second byte will contain the number of characters that were read. I hope you see now why you need lea dx,str2+2. That's the address where the characters are.
Please note that with a first byte of 50 input is limited to 49 characters as DOS appends a terminating '$' character (Not included in the second byte).

FASM: macro to display string

I want to create macro in FASM, which could directly print string (int DOS) like this:
prints 'hey there!!!!'
I have written such code:
format MZ
use16
stack 0x100
entry _TEXT#16:_start
;
macro prints str
{
call #f
db str, 0x24
##:
pop dx
mov ah, 9
int 0x21
}
segment _DATA#16 use16
msg db 'hi!', 0xd, 0xa, 0x24
segment _TEXT#16 use16
_start:
push _DATA#16
pop ds
prints 'hi there))) !!!!'
prints 'me'
mov ax, 0x4c00
int 0x21
ret
The problem is: when I leave my _DATA#16 segment empty (without any variables) all is fine.
But when I define new variable in that segment some raw extra symbols begin to appear like this: http://board.flatassembler.net/files/err_758.png
So can you help me? where is my mistake?
Maybe I have chosen the wrong way to achieve that thing I want?
Help please....
As far as I understood it is because int 21h expects offset in _DATA#16 segment but not _CODE#16 segment. So, the easiest way - to use only one segment in program or just using .com files. Here is sample:
use16
org 0x100
macro prints [str*]
{
pusha
if str in <0xd, 0xa, 9>\
| str eqtype ''
call #f
db str, 0x24
##:
pop dx
else
mov dx, str
end if
mov ah, 9
int 0x21
popa
}
_start:
prints 0xd, 0xa, 9
prints 'hi!', 0xd, 0xa
mov ax, msg
prints ax, 0xd, 0xa
prints msg
int 0x20
ret
msg db 'hey there!', 0x24
It can accept strings directly, addresses of strings in registers and variables.
It can also handle 3 special characters - 0xd (CR), 0xa (LF) and 9 (TAB).
If I find the way to display string directly in multi-segment programs, I will post the answer.

Resources