Author Topic: Writing to a file with NASM  (Read 13065 times)

Offline VernonRoche

  • Jr. Member
  • *
  • Posts: 4
Writing to a file with NASM
« on: November 18, 2014, 09:05:04 PM »
So I need to write to a file with NASM, however while I can generate the file I'm having trouble getting it to write in it. I haven't had any segmentation faults or anything of the sort, nonetheless, after my whole program ends its execution, nothing is written to the file. Here's how my program works, it asks for a file name and an amount of random characters to generate. Afterwards, it generates the file, then the random number, executes a loop, and within that loop it is supposed to write each letter to the file. However, my issue is that it doesn't write anything at all, it compiles well and all, but nothing is ever written to the file.

I suspect this is probably because of the way I have allocated memory, I'm not at all really sure about how to do that correctly. Regardless, here is my code:
Code: [Select]
%include "io.mac"

.DATA
filename_msg db 'Enter the file name: ', 0
number_prompt_msg db 'Enter the number of bases: ',0 ;asks for the number of bases to be used
finish_msg db 'Operation completed, DNA file generated',0 ;tells the user when the file is complete
error_msg db 'Operation failed, please try again', 10
file_error_msg db 'Write operation error',10
base_A db 'A',0
base_C db 'C',0
base_G db 'G',0
base_T db 'T',0
base_length equ $ - base_A
;----------------------------------------------------------------------------------------------------
.UDATA
number_of_bases rest 1 ;defined by the user
random_number resd 1   
filename: resd 20       ;defined by the user
base resb 1
file_descriptor resd 1    ;used to generate the file
loop_number resd 1
;-------------------------------------------------------------------
;start of code, and message prompts for the user
.CODE

.STARTUP

;asks user for filename
ask_details:
PutStr filename_msg
GetStr filename, 300

;asks user for the number of bases
PutStr number_prompt_msg
GetLInt [number_of_bases]

;------------------------------------------------------------
;file creation
mov EAX, 8 ;creates the file
mov EBX, filename
mov ECX, 644O   ;octal instruction
int 80h ;kernel interrupt
cmp EAX,0 ;throws error if something is amiss
jbe error
mov [file_descriptor],EAX
mov ECX,[number_of_bases]
;-------------------------------------------------------------
;randomization of base numbers
writing_loop:
rdtsc
mov EAX, EDX
add EAX, ECX
mov EDX, 0
div ECX
mov EDX, 0
mov EBX, 4
div EBX
mov [random_number], EDX
mov EDX, 0
mov EAX,[random_number]
cmp EAX,0
je assignment_A
cmp EAX,1
je assignment_C
cmp EAX,2
je assignment_G
cmp EAX,3
je assignment_T
write_char:
mov [base],EBX  ;moves the letter to base for later use
dec ECX         ;subtracts 1 to ECX to use in loop
mov [loop_number],ECX ;stores loop because ECX is required to write to file
PutStr base
;-------------------------------------------------------------------
;write to file
;also opens the file 
mov EAX,4
mov EBX,[file_descriptor]
mov ECX, base
mov EDX, base_length
int 80h
cmp EAX, 0
jbe file_write_error
;close file
mov EAX,1
mov EBX, 0

;continue loop
mov ECX,[loop_number]
cmp ECX,0
jne writing_loop
jmp done
;------------------------------------------------------------
;file generation error message
error:
PutStr error_msg
jmp ask_details
;------------------------------------------------------------
;file write error
file_write_error:
PutStr file_error_msg
jmp done
;------------------------------------------------------------
done:
nwln
.EXIT
;------------------------------------------------------------
;assignments
assignment_A:
mov EBX, [base_A]
jmp write_char

assignment_C:
mov EBX, [base_C]
jmp write_char

assignment_T:
mov EBX, [base_T]
jmp write_char

assignment_G:
mov EBX, [base_G]
jmp write_char

Most of this I had to figure out on my own, I did not get much help from my teacher on this matter, I'm not sure what is really going on and why nothing is being written. When I print the output on the console it shows every character that has been selected, so the letters are working. I just need to get this done, and I'm finished with the hard part.

This is the relevant part to writing, I'm sure I put everything as it needs to be, but I'm not sure.

Code: [Select]
;write to file
;also opens the file 
mov EAX,4
mov EBX,[file_descriptor]
mov ECX, base
mov EDX, base_length
int 80h
cmp EAX, 0
jbe file_write_error
;close file
mov EAX,1
mov EBX, 0

EDIT: This is what I get if I assign more memory, so this is definitely something I'm doing wrong with my memory allocations.

Code: [Select]
T            20
.txt
         G            20
.txt
         C            20
.txt
         G            20
.txt
         T            20
.txt
         C            20
.txt
         T      
      20
.txt
         C             20
.txt
         T             20
.txt
         T      
      20
.txt
         C             20
.txt
         C            20
.txt
         G            20
.txt
         G            20
.txt
         C            20
.txt
         C            20
.txt
         T            20
.txt
         C            20
.txt
         C            20
.txt
         G             20
.txt
         

Empty spaces are supposed to be null.

EDIT 2: I'm working with Linux on this.
« Last Edit: November 18, 2014, 10:43:36 PM by VernonRoche »

Offline gammac

  • Jr. Member
  • *
  • Posts: 71
  • Country: 00
Re: Writing to a file with NASM
« Reply #1 on: November 18, 2014, 10:38:08 PM »
Hi VernonRoche,

I saw a fault.

Code: [Select]
base_A db 'A',0
base_C db 'C',0

...

;assignments
assignment_A:
mov EBX, [base_A]
jmp write_char


You read 4 bytes into ebx.


Code: [Select]
write_char:
mov [base],EBX  ;moves the letter to base for later use

You write 4 bytes to address base

Code: [Select]
base resb 1
file_descriptor resd 1    ;used to generate the file
loop_number resd 1

You have reseved 1 byte at this address. You overwrite the filedescriptor.

Maybe there is more what's wrong, I don't know.

Is this for a unix machine?
Please comment your code! It helps to help you.

Offline VernonRoche

  • Jr. Member
  • *
  • Posts: 4
Re: Writing to a file with NASM
« Reply #2 on: November 18, 2014, 10:46:47 PM »
Yeah I'm working with UNIX. I've had to figure out most of this on my own. And I'm not real sure what's going on with this thing to be honest. I'm trying my best though, as this is really the last thing I need to have done with NASM for now. Once I figure out how to write it into the file then its done.

EDIT: Assigning memory correctly, somewhat worked, I'm still getting some null spaces and some strange looking scribbles though. I'm really bad with the memory allocation, there's just a lot of stuff, I forget when it comes to it.

EDIT 2: I did this though:
Code: [Select]
base resb 4
« Last Edit: November 18, 2014, 11:08:28 PM by VernonRoche »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Writing to a file with NASM
« Reply #3 on: November 19, 2014, 12:20:00 AM »
Hi VernonRoche,

Good to see you here! Your code is much improved from SO. Still some problems, as you know. I have modified your code slightly - more than was needed probably - tried to keep as much of your code as possible. I made quite a mess, but it "seems to work". (haven't tested it much)

Code: [Select]
; nasm -f elf32 myfile.asm -I(path to io.mac)/
; ld -o myfile myfile.o (path to io.o)/io.o (for 64-bit sytems: -m elf_i386)

%include "io.mac"

.DATA
filename_msg db 'Enter the file name: ', 0
number_prompt_msg db 'Enter the number of bases: ',0 ;asks for the number of bases to be used
finish_msg db 'Operation completed, DNA file generated',0 ;tells the user when the file is complete
error_msg db 'Operation failed, please try again', 10
file_error_msg db 'Write operation error',10
base_A db 'A',0
base_C db 'C',0
base_G db 'G',0
base_T db 'T',0
base_length equ $ - base_A
;----------------------------------------------------------------------------------------------------
.UDATA
number_of_bases rest 1 ;defined by the user
random_number resd 1   
filename: resd 20       ;defined by the user
base resb 100 ; 1 definitely not enough! is this enough?
file_descriptor resd 1    ;used to generate the file
loop_number resd 1
;-------------------------------------------------------------------
;start of code, and message prompts for the user
.CODE

.STARTUP

;asks user for filename
ask_details:
PutStr filename_msg
GetStr filename, 300 ; you only allow 20 bytes for this!

;asks user for the number of bases
PutStr number_prompt_msg
GetLInt [number_of_bases]

;------------------------------------------------------------
;file creation
mov EAX, 8 ;creates the file
mov EBX, filename
mov ECX, 644O   ;octal instruction
int 80h ;kernel interrupt
cmp EAX,0 ;throws error if something is amiss
jbe error
mov [file_descriptor],EAX
mov ECX,[number_of_bases]
xor esi, esi ; we will use this as index into "base"
;-------------------------------------------------------------
;randomization of base numbers
writing_loop:
rdtsc
mov EAX, EDX
add EAX, ECX
mov EDX, 0
div ECX
mov EDX, 0
mov EBX, 4
div EBX
mov [random_number], EDX
mov EDX, 0
mov EAX,[random_number]
cmp EAX,0
je assignment_A
cmp EAX,1
je assignment_C
cmp EAX,2
je assignment_G
cmp EAX,3
je assignment_T
write_char:
mov [base + esi],bl ;EBX  ;moves the letter to base for later use
inc esi ; next index
dec ECX         ;subtracts 1 to ECX to use in loop
mov [loop_number],ECX ;stores loop because ECX is required to write to file

; since I moved the sys_write out of the loop, we no longer need
; [loop_number] - does no harm...

;continue loop
mov ECX,[loop_number]
cmp ECX,0
jne writing_loop

; moved out of loop
mov byte[base + esi], 0 ; zero-terminate for PutStr - sys_write doesn't need it
PutStr base
;-------------------------------------------------------------------
;write to file
;also opens the file
; (no it doesn't!)
mov EAX,4
mov EBX,[file_descriptor]
mov ECX, base
; mov EDX, base_length ; not actual length of "base"!
mov edx, esi ; this should be?
int 80h
cmp EAX, 0
jbe file_write_error

;close file
; mov EAX,1
; mov EBX, 0
; comment does not match code!
;mov eax, 6 ; sys_close
;mov ebx, [file_descriptor]
;int 80h
; this turns out not to be neccessary
; probably still a good idea

jmp done
;------------------------------------------------------------
;file generation error message
error:
PutStr error_msg
jmp ask_details
;------------------------------------------------------------
;file write error
file_write_error:
PutStr file_error_msg
jmp done
;------------------------------------------------------------
done:
nwln
.EXIT
;------------------------------------------------------------
;assignments
assignment_A:
mov bl, [base_A]
jmp write_char

assignment_C:
mov bl, [base_C]
jmp write_char

assignment_T:
mov bl, [base_T]
jmp write_char

assignment_G:
mov bl, [base_G]
jmp write_char

There are other issues we might discuss... how long do you really want the filename to be? How many bases do you want to allow? Other things... If this is your "last" project for Nasm, you may not want to bother. I'm gonna grab something to eat...

Best,
Frank


Offline VernonRoche

  • Jr. Member
  • *
  • Posts: 4
Re: Writing to a file with NASM
« Reply #4 on: November 19, 2014, 12:40:12 AM »
Oh no, I think I explained that wrong, what I meant by last thing to do is that I only need it to write to the file, and I'm finished with the project. Anyway, the file name's length depends on what the user may type. The same goes for the bases, they might be 50 or 5000, it all depends on how many the user might to use.

I did figure out that writing to a file wrote the whole thing instead of just the letters, hence the null values in there. I figured I'd have to tell it to write the individual bytes instead of the whole thing. Anyway, I appreciate the time you took to help me out. 

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Writing to a file with NASM
« Reply #5 on: November 24, 2014, 07:33:03 AM »
Ahhhm. I'm awake!

Hi VernonRoche,

No problem, this is my "hobby"... when I'm in the mood. Been having trouble getting "in the mood" lately...

We've been exchanging some private messages, in which I said there were some interesting issues to discuss in your project, but your last message indicates that time is running short, So I'd better jump forward and get back to the interesting issues later. Here's the last code you sent me:

Code: [Select]
;dynamic memory allocation for base
xor EBX,EBX  ;zeroes EBX
mov EAX,45   ;sys_brk call
int 80h
 
mov [base],EAX
mov EBX,EAX
pop EBX
add EBX, number_of_bases
mov EAX, 45
int 80h

I don't see what's being popped into ebx here, and you're adding the address of number_of_bases. I'd try something like...
Code: [Select]
;dynamic memory allocation for base
xor EBX,EBX  ;zeroes EBX
mov EAX,45   ;sys_brk call
int 80h
 
mov [orig_break],EAX

mov EBX,EAX
add EBX, [number_of_bases]
mov EAX, 45
int 80h
        mov [current_break], eax

There are still a couple of problems with this. One is that number_of_bases is a 64-bit number (qword). I just noticed that you're using Dr. Dandamudi's "getLInt" to get it. We've been using it as if it were a 32-bit number. This isn't going to cause a problem unless the pesky user enters a number bigger than four billion and change (don't expect me to memorize all those digits). If that happens... we can handle a 64-bit number in 32-bit code by using 2 registers (or memory if we're out if registers), but we haven't got that much memory to allocate in any case. I think we're in big trouble if the assignment insists on a 64-bit number here! Possible ways to handle that... later.

Also, I don't show any error checking. I've just noticed a subtle bug in your earlier code...
Code: [Select]
cmp eax, 0
jbe error

The subtle bug is that "ja" and "jb" check the flags after an unsigned comparison, "jg" and "jl" are for a signed comparison. The above will jump if eax is zero, but not if it's "negative" - since it's unsigned it will never be negative. "jl" should work for you (zero may not be an error, so you may not want "jle"). That's a "quick and dirty" way to do it. Actually, some system calls can return numbers greater than 2G, and not be an error. Error numbers are actually from -4095  up to zero. I sometimes use:
Code: [Select]
int 80h
cmp eax, -4096
ja error
That looks confusing since I use a negative number and an unsigned condition code. Probably better to write it as 0xFFFFF000 - it's the same number (barring typos or thinkos). I often use the "quick and dirty" method. Actually...
Code: [Select]
int 80h
test eax, eax ; just to set the flags
js error
This "usually works" but sys_brk is one of the calls that could return over 2G - "negative" and not be an error.

Worse, I have recently read (somewhere) that sys_brk doesn't return -ERRNO but merely returns the same break as before. This is going to require some different error checking.

At one point, you seemed to be trying to stuff "new break" into [base], expecting that to enlarge our buffer. I think you've figured out that's not going to work. We've reserved what we've reserved, and it can't be changed at run-time. Your "new buffer" (which hasn't got a name) runs from "old break" to "new break".

You showed me some code (which I may post later) in which you set edi to the "new break", set the direction flag to down (std), and use stosb to fill the buffer from the top down. That should work, and seems like a clever idea to me. I'll look more at that.

You mentioned - way back on SO - that you couldn't write one byte at a time to the file because you'd used all the registers. Actually, you can. It would be very slow(!) but would work up to however much disk space you've got without worrying about how much memory you've got. You could also reserve a buffer - a few thousand bytes, say - fill it with bases, write it to the file when full, go back and fill it up again, write it... until done. This may be the best method - especially if we have to deal with more than 4G of bases!

More to come... Rattle my cage if I don't get back here!

Best,
Frank










Offline VernonRoche

  • Jr. Member
  • *
  • Posts: 4
Re: Writing to a file with NASM
« Reply #6 on: November 24, 2014, 07:16:29 PM »
I think this will function well enough. Besides, yesterday, I realized that what I'm being asked to do, isn't exactly possible, not even with normal languages and I believe that stems from the fact that my professor doesn't seem to know much about assembler. Basically what he thinks that we can do is that we should be able to reserve whatever memory space is required during run time through the code segment. After hitting my head against the wall yesterday like for 12 hours I realized, that this just doesn't work. In short what he thinks we should do, is sort of what someone would do in Java or C++, in which they would declare an int/long/double to be able to read the size of the user's input. However after thinking about it for a while, those are still not dynamic in the sense that he's asking, they're still set memory spaces.

In short what he sort of wants is doing a resb N(chose resb as an example, I guess it could be anything though), with N being whatever memory is required, within the code segment. However, that isn't possible, as far as I'm aware, plus I'm also not very sure its a good idea to let the program reserve memory depending on the user's whims, especially if said users were not familiarized with an assembler, and I'm willing to bet most users aren't. I will put what you have written into my code, but don't worry, I think you've helped me out a lot. Same goes for the matrix, there's just no way that I see it possible to do that. In fact I doubt even malloc() could have done the job, I thought that this would just enlarge the buffer, but apparently it would put more memory, well once I figured out that doing it in another language would yield the same result.

EDIT: I should also mention that I looked at a particular example for my attempts, I was also not sure what that pop EBX was for, I never saw a push EBX in this example, but at that point I was just not thinking straight from trying to make this happen. Anyway here it is:

http://www.dreamincode.net/forums/topic/287274-nasm-linux-dynamic-memory-allocationread-file/
« Last Edit: November 24, 2014, 07:30:17 PM by VernonRoche »

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Writing to a file with NASM
« Reply #7 on: November 24, 2014, 10:04:30 PM »
Okay, we can discuss Gunner's code a little (great example - thanks Gunner!)...
Code: [Select]
;~ Get end of bss section
xor ebx, ebx
mov eax, sys_brk
int 80H
mov [Org_Break], eax
mov [TempBuf], eax
push eax
There it is, right there. It wasn't in ebx, of course - we don't have to "pop" something into the same register it was pushed from - or any register... we can "pop" to memory, too. Just a way of saving a value temporarily. As it happens, Gunner doesn't modify eax - he could have done just "mov ebx, eax" with the same effect - but he could have. He's making his code so that it won't break if he modifies it. A wise idea!
Code: [Select]
; extend it by file size
pop ebx  ; here's our "old break"
add ebx, dword [stat + STAT.st_size]
mov eax, sys_brk
int 80H
Almost exactly what you want to do, except that you want to add [number_of_bases] instead of a file size.

The important thing for you to "get" is that TempBuf is a "pointer to buffer" and not the "new" buffer itself. Watch how he uses it:
Code: [Select]
: in a sys_read
mov ecx, [TempBuf] ; "[contents]" of TempBuf
If he'd done:
Code: [Select]
mov ecx, TempBuf ; address of TempBuf
He'd have been using TempBuf as the buffer - just what you don't want!

More to come...

Best,
Frank


Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Writing to a file with NASM
« Reply #8 on: November 26, 2014, 01:04:09 AM »
I'm sorry, I'm having trouble keeping up with this. I know time is getting short for you. How's it going?

Best,
Frank