The programs we do in this book aren't complicated enough to necessitate a memory manager. Therefore, we will just use our memory manager to allocate a buffer for one of our file reading/writing programs instead of assigning it in the .bss.
The program we will demonstrate this on is read-records.s from Chapter 6. This program uses a buffer named record_buffer to handle its input/output needs. We will simply change this from being a buffer defined in .bss to being a pointer to a dynamically-allocated buffer using our memory manager. You will need to have the code from that program handy as we will only be discussing the changes in this section.
The first change we need to make is in the declaration. Currently it looks like this:
.section .bss .lcomm, record_buffer, RECORD_SIZE
It would be a misnomer to keep the same name, since we are switching it from being an actual buffer to being a pointer to a buffer. In addition, it now only needs to be one word big (enough to hold a pointer). The new declaration will stay in the .data section and look like this:
record_buffer_ptr: .long 0
Our next change is we need to initialize our memory manager immediately after we start our program. Therefore, right after the stack is set up, the following call needs to be added:
call allocate_init
After that, the memory manager is ready to start servicing memory allocation requests. We need to allocate enough memory to hold these records that we are reading. Therefore, we will call allocate to allocate this memory, and then save the pointer it returns into record_buffer_ptr. Like this:
pushl $RECORD_SIZE call allocate movl %eax, record_buffer_ptr
Now, when we make the call to read_record, it is expecting a pointer. In the old code, the pointer was the immediate-mode reference to record_buffer. Now, record_buffer_ptr just holds the pointer rather than the buffer itself. Therefore, we must do a direct mode load to get the value in record_buffer_ptr. We need to remove this line:
pushl $record_buffer
And put this line in its place:
pushl record_buffer_ptr
The next change comes when we are trying to find the address of the firstname field of our record. In the old code, it was $RECORD_FIRSTNAME + record_buffer. However, that only works because it is a constant offset from a constant address. In the new code, it is the offset of an address stored in record_buffer_ptr. To get that value, we will need to move the pointer into a register, and then add $RECORD_FIRSTNAME to it to get the pointer. So where we have the following code:
pushl $RECORD_FIRSTNAME + record_buffer
We need to replace it with this:
movl record_buffer_ptr, %eax addl $RECORD_FIRSTNAME, %eax pushl %eax
Similarly, we need to change the line that says
movl $RECORD_FIRSTNAME + record_buffer, %ecx
so that it reads like this:
movl record_buffer_ptr, %ecx addl $RECORD_FIRSTNAME, %ecx
Finally, one change that we need to make is to deallocate the memory once we are done with it (in this program it's not necessary, but it's a good practice anyway). To do that, we just send record_buffer_ptr to the deallocate function right before exitting:
pushl record_buffer_ptr call deallocate
Now you can build your program with the following commands:
as read-records.s -o read-records.o ld alloc.o read-record.o read-records.o write-newline.o count- chars.o -o read-records
You can then run your program by doing ./read-records.
The uses of dynamic memory allocation may not be apparent to you at this point, but as you go from academic exercises to real-life programs you will use it continually.