Hi Bryant,
1. Got it to work using your stuff. I think.
Now if Linux Mint gets corrupted I'll be able to reinstall it
without losing the goodies in NASM.
I compiled the demo64 program on the HD separate from Linux.
That worked fine. When I tried to execute the program I got
the following:
jack@jack-myheadache /media/jack/NASM/nasmfld $ ./demo64
bash: ./demo64: Permission denied
jack@jack-myheadache /media/jack/NASM/nasmfld $
Is it possible that I was not in administrator mode??
Don't know how to get there in Linux.....
Maybe you should try this command:
$ sudo chown -R jack /media/jack
That should recursively scan through all files and directories in /media/jack and make the file/directories' owner jack.
2. Trying to understand your explanation of adding colors to the screen.
When I do man wchgat all I get is a screen showing data for "attr"?
Documentation for that routine is in the same manual entry for all attribute changing routines (including attron, attroff, etc.) Scrolling down a bit, you'll see the Synopsis for wchgat is:
int wchgat(WINDOW *win, int n, attr_t attr,
short color, const void *opts)
And the explanation is bit further down:
The routine chgat changes the attributes of a given number of charac?
ters starting at the current cursor location of stdscr. It does not
update the cursor and does not perform wrapping. A character count of
-1 or greater than the remaining window width means to change at?
tributes all the way to the end of the current line. The wchgat func?
tion generalizes this to any window; the mvwchgat function does a cur?
sor move before acting. In these functions, the color argument is a
color-pair index (as in the first argument of init_pair, see col?
or(3NCURSES)). The opts argument is not presently used, but is re?
served for the future (leave it NULL).
The striked out section should be ignored. The features of NCURSES documented with the 'mv' prefix are implemented as C macros, so in assembly you have to call the wmove procedure separately.
3. In Masm32 I avoided Macros like the plague. I like to see, in my code,
the full code for anything that needs repeated. I would write it once
and branch to and return from it as many times as needed. This way I
don't have to go digging for what the macro does and it's parameters.
In NASM I have trouble knowing what is a marco and what are it's
parameters and where to find data on the macro.
This is my problem right now with "get_cur_coord".
Thanks for your help.
There is no need to "dig" for what the macro does and it's parameters, I gave you the macro, I told you the parameters.
Do us both a favor and re-read the reply I referred you to. For clarity, I'll repost the link:
http://forum.nasm.us/index.php?topic=2204.msg9799#msg9799I'll take partial blame for this, maybe it's my coding style that is throwing you off. I'll give you a quick introduction into the process I use to create procedures and/or macros. From this point on, I'll refer to procedures and macros as
processes, not to be confused with
tasks which are sometimes also called processes.. In any event, just note that when I use the word
process in the following, I mean any type of procedure, function, or macro.
Step 1. - Interface Specification.
An interface specification is a
contract you make with other developers (or yourself) about how the code you plan to write will be used. In general, the interface specification needs to define the name of the process, the inputs that are consumed by the process, and the outputs that are produced as a result of the process' execution. There are many styles of writing interface specifications, but the one I use is the single-line form:
;; process_name : inputs -> outputs
When you see this type of interface specification, it reads as "process_name consumes inputs and produces outputs". So, for example, when I wrote
get_cur_coord macro, I began by writing the comment line:
;; get_cur_coord : window_handle -> { x y }
Like before, this should read as "get_cur_coord consumes window_handle and produces x and y". This means that the macro takes a single argument (the window handle) and returns to you the values of X and Y.
Step 2. - Process Description.
This is a very important step! Many programmers will skip that first step assuming that the reader of the source code can infer from context the inputs and outputs, but no programmers with any real experience will leave out the process description in software they expect people to regularly use. The process description extends on the interface specification by giving a general description of what the process expects to accomplish. This is usually a paragraph that appears just below the interface specification. People who leave out the interface specification are usually taught to put this description within the code block before any actual code (you'll see people do that a lot in Visual Basic). I use my interface specifications, so I put it after them. In our
get_cur_coord example, it reads;
;; Obtains the current X and Y coordinates from a window handle
;; and returns the current X coordinate in rax and the current
;; Y coordinate in rbx.
This process description tells you that this process will obtain an X and Y coordinate from a window handle, then goes on to further explain that the X value will be located in the RAX register and the Y value in the RBX. From this description, you should be able to infer how to use the process. If we write the code:
get_cur_coord [hWindow]
After this process, the value of RAX is the X coordinate and the value of RBX is the Y coordinate.
Step 3. - Examples.
I think this is where I messed up in my code, I left this part out. When i post code on the forum, I tend to remove this comment to reduce the overall code size. I have a tendency to be
verbose, or long-winded to use a local term, and I regularly hit the post length limit. So I reduce code by stripping out stuff i think people aren't going to care much about; this always includes the example section. However, it's an important part of my development process and I think I should at least explain it.
At this point in the design process, I'll take the information I've accumulated so far (the interface specification and process description) and formalize the expectations of the process. The reason I skipped this in the
get_cur_coord example is simply because I was too lazy to write one. This is a bad excuse, but writing examples for processes which have "artifacts" or have some form of internal state are difficult to write. A good example of this difficulty would be a process for a random number generator. It's hard to describe a behavioral expectation for something that has an unpredictable behavior. The general layout of an example section (at least in my code) will usually be a list of uses with an '=>' followed by the expected result. If I was to give this a try with the
get_cur_coord macro, I'd probably implement it as:
;; given
;; wmove (hWindow, 0, 0); get_cur_coord [hWindow] => { RAX: 0, RBX 0 },
;; wmove (hWindow, 5, 4); get_cur_coord [hwindow] => { RAX: 4, RBX 5 }, and
;; wmove (hWindow, LINES, COLS); get_curr_coord [hWindow] => { RAX: COLS, RBX: LINES }.
This comment should assume nothing about the calling conventions used, so in this example you can see I'm referencing a call to wmove as though it was a C routine. The idea here is that we imply enough information about the use that we can formalize some form of test cases.
Step 4. - Unit Testing.
This is another section which I didn't implement in my example, and you'll rarely ever see in any code you'll download. The purpose of the unit testing code is to implement a series of processes which do exactly what your examples earlier did. It may seem odd at first that we are developing test cases before we develop the actual code, but there is valid reasoning here. The unit tests should be an un-biased
VALIDATION that our implementation is properly working. There is an overwhelming urge to use our implementation to design our test cases, though this would be considerably easier, it would increase the odds that a flawed implementation detail could be overlooked.
Our expected unit test would look something like this:
get_cur_coord_ut:
STACK_ALIGN
;; given wmove (hWindow, 0, 0); get_cur_coord [hWindow]
mov rdx, 0
mov rsi, 0
mov rdi, [hWindow]
call wmove
get_cur_coord [hWindow]
;; expect { RAX: 0, RBX 0 }
cmp rax, 0
jne .FAIL
cmp rbx, 0
jne .FAIL
;; given wmove (hWindow, 5, 4); get_cur_coord [hwindow]
mov rdx, 4
mov rsi, 5
mov rdi, [hWindow]
call wmove
get_cur_coord [hWindow]
;; expect { RAX: 4, RBX 5 }
cmp rax, 4
jne .FAIL
cmp rbx, 5
jne .FAIL
;; given wmove (hWindow, LINES, COLS); get_curr_coord [hWindow]
mov rdx, [COLS]
mov rsi, [LINES]
mov rdi, [hWindow]
call wmove
get_cur_coord [hWindow]
;; expect { RAX: COLS, RBX: LINES }
cmp rax, [COLS]
jne .FAIL
cmp rbx, [LINES]
jne .FAIL
.SUCCESS:
mov rax, TRUE
jmp .DONE
.FAIL:
mov rax, FALSE
.DONE:
STACK_RESTORE
ret
At this point, when we implement the process, we now have a method of validating whether the implementation satisfies our initial expectations or not.
Step 5. - Process Implementation.
Once we have designed all this support information, we can design the process implementation itself. In my earlier post, i gave you the interface specification, the description, and the process implementation. In this case, I looked up the implementation details of NCURSES' window structure and saw that the current Y coordinate is the first 16-bit element and the current X coordinate is the second 16-bit element of the data structure. That means, our use case for this process is to consume (as input) a window handle and produce (as output) the first element of that structure in BX and the second element of that structure in AX. Because RAX and RBX may already contain values, we need to clear the registers before retrieving the elements. So, to summarize:
Use-Case:
1. Obtain reference to our window handle.
2. Clear RAX and RBX registers.
3. Assign first 16-bit value in structure to BX.
4. Assign second 16-bit value in structure to AX.
To do this, we'll need to use a register for dereferencing the window handle and that register should probably be saved and restored in case it's currently being used. This changes our use-case to:
Use-Case:
1. Save RSI register.
2. Obtain reference to our window handle and put it in RSI.
3. Clear RAX and RBX registers.
4. Assign first 16-bit value in structure to BX.
5. Assign second 16-bit value in structure to AX.
6. Restore RSI register.
This use-case analysis finally lead me to my implementation which was a transformation of the steps to instructions:
%macro get_cur_coord 1
push rsi
mov rsi, %1
xor rax, rax
xor rbx, rbx
mov ax, [rsi + 2]
mov bx, [rsi]
pop rsi
%endmacro
This could also have been implemented using a procedure (note: the calling convention takes parameters in RDI so we'll just use that instead of RSI).
;; get_cur_coord_proc : window_handle -> { x y }.
;; Obtains the current coordinates from a window handle in RDI
;; and returns the current x-coordinate in RAX and the current
;; y-coordinate in RBX.
;; given
;; wmove (hWindow, 0, 0); get_cur_coord_proc (hWindow) => { RAX: 0, RBX 0 },
;; wmove (hWindow, 5, 4); get_cur_coord_proc (hwindow) => { RAX: 4, RBX 5 }, and
;; wmove (hWindow, LINES, COLS); get_curr_coord_proc (hWindow) => { RAX: COLS, RBX: LINES }.
get_cur_coord_proc:
STACK_ALIGN
xor rax, rax
xor rbx, rbx
mov ax, [rdi + 2] ; second element (x-coord)
mov bx, [rdi] ; first element (y-coord)
STACK_RESTORE
ret
This would, of course, require our unit test code to be changed.
In any event, I hope this clears up any confusion as to my coding style when trying to read examples I provide you. You're not the first to mention difficulty in understanding my code. It helps to point out that most of my "design" techniques come from functional programming whereas most of my implementation practices come from assembly language and a few high level structured languages. I tend to assume that people can infer the meaning of my code from context, but that is rarely the case.
As a reminder, be sure to review the previous reply I referred to:
http://forum.nasm.us/index.php?topic=2204.msg9799#msg9799
Regards,
Bryant Keller