So, thanks again. What I've learned so far is that the FPU forces the significand to be in the range 1<S<2. I'm still not sure why, but will continue studying this.
I wrote a little testbed program (attached here) to demonstrate (to myself) how this all works. Discovered that FSCALE is essentially (completely?) the inverse of FXTRACT. All this program does is show the result of splitting a number with FXTRACT and then "re-assembling" it with FSCALE.
So about this testbed program: So long as I'm posting it here, I thought I'd explain a little about it. It shows my coding style, which may be useful for anyone who's searching for their own style and is not sure how they want their code to look. Let me take pains to point out that I am not proposing this as the way everyone should write assembly language; far from it. If you're an experienced coder, you have no doubt developed the style that you prefer and don't need any advice from the likes of me. But if you're just starting out, there may be some useful things in my style for you to borrow.
Here's what I do:
I prefer to write ASM statements in caps, with variable names in mixed case (upper and lower):
;============= Message loop ===================
msgloop:
INVOKE GetMessage, ADDR msg, NULL, 0, 0
OR EAX, EAX ;EAX = 0 = exit
JZ exit99
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
JMP msgloop
I guess to me that looks more "assembler-ish", and easier to read.
Variable names:
I distinguish between global variables, which I start with a capital letter, and local variables, which start lowercase:
MOV EAX, lParam ;lParam is a local (actually a function argument)
CMP EAX, SigRawOutHandle ;SigRawOutHandle is a global
and a third type of identifier is a constant (an EQU-defined number):
MOV EDX, $mainWinWidth
where this constant is defined thus:
$mainWinWidth EQU 370
To me, this really helps to figure out what kind of "object" I'm dealing with, whether it's a local variable, a global or a defined constant (not a variable at all).
Which brings us to EQUs in general; use them, instead of sticking constants (numbers) all over the place in your code. Two reasons: one, they give you an idea of what they mean when you read them instead of some possibly meaningless number, and two, if it's a constant that's used in more than one place and you ever need to change it, it's much easier to just change the EQU rather than having to hunt down every instance of the number.
Comments: Use them!
I know some of us feel like we're too cool to comment our own code. I've read some of this code: it sucks! Maybe it works now, and maybe you understand it now, but try coming back to it in five or ten years and see if you really understand that really clever thing you did with bit-twiddling or whatever. The person that comments will help is you. At least that's my experience. You don't have to comment each and every line of code, but do put comments wherever something might not be obvious to whoever's reading it (which, as I pointed out, might be you some years from now).
And something I really like to do in code is make it data-driven by putting stuff into data structures rather than code. In this li'l program, it's how all the control windows (static, edit, button) get created. In the data segment, there's a structure that contains all the parameters for all these controls:
ControlParams LABEL DWORD
$CWP <20, 22, 40, 12, $PiRadio, ButtonClassName, PiRadioTxt, $radioStyles_GROUP, NULL, PiRadioHandle>
$CWP <60, 22, 60, 12, $NumRadio, ButtonClassName, NumRadioTxt, $radioStyles, NULL, NumRadioHandle>
$CWP <120, 19, 80, 18, $NumEdit, EditClassName, NULL, $editStyles, NULL, NumEditHandle>
$CWP <245, 18, 40, 20, IDOK, ButtonClassName, GoBtnTxt, $buttonStyles, NULL, GoBtnHandle>
$CWP <120, 60, 150, 16, 100, StaticClassName, ResultsTxt, $staticStyles, NULL, NULL>
$CWP <90, 80, 60, 16, 101, StaticClassName, RawTxt, $staticStyles, NULL, NULL>
$CWP <240, 80, 45, 16, 102, StaticClassName, DecTxt, $staticStyles, NULL, NULL>
$CWP <20, 100, 20, 16, 103, StaticClassName, SigTxt, $staticStyles, NULL, NULL>
$CWP <45, 100, 120, 16, $sigRawOut, StaticClassName, NULL, $staticStyles, NULL, SigRawOutHandle>
$CWP <190, 100, 20, 16, 104, StaticClassName, SigTxt, $staticStyles, NULL, NULL>
$CWP <215, 100, 120, 16, $sigDecOut, StaticClassName, NULL, $staticStyles, NULL, SigDecOutHandle>
$CWP <20, 120, 20, 16, 105, StaticClassName, ExpTxt, $staticStyles, NULL, NULL>
$CWP <45, 120, 120, 16, $expRawOut, StaticClassName, NULL, $staticStyles, NULL, ExpRawOutHandle>
DD -1 ;End of list marker
Then when it's time to create these controls, all that's needed is:
; Create control windows from control-parms struct:
PUSH EBX
LEA EBX, ControlParams
nxtwin: INVOKE CreateWindowEx,
0, ;WS_EX_xxx styles
[EBX].$CWP.classNamePtr, ;ptr. to Win32 class name
[EBX].$CWP.textPtr, ;ptr. to window name/text
[EBX].$CWP.styles, ;WS_xxx styles
[EBX].$CWP.x, ;X-position
[EBX].$CWP.y, ;Y-position
[EBX].$CWP.wdth, ;width
[EBX].$CWP.hght, ;height
MainWinHandle, ;parent window
[EBX].$CWP.ID, ;Menu/ID
hInst, ;Instance handle
NULL ;lParam pointer (not used)
; Move to next item in list:
ADD EBX, SIZEOF $CWP
CMP [EBX].$CWP.x, -1 ;End o'list?
JNZ nxtwin
POP EBX
See how simple that is? The alternative would be many, many CreateWindowEx() blocks, each with all those arguments. Not fun!
By the way, the program shows how to create a "dialog" without an actual dialog, and without using the resource compiler. I did this since this was just a quick-and-dirty app. It took maybe an hour or so to line everything up nicely (I used WinSpy to select each control and actually move it on-screen until it was where I wanted it). I don't recommend this technique for any kind of production code, but this shows that it can be done if you want.
Last thing: I put a little "extra credit" stuff in which is the code that shows the result fields (which are just static controls that I set with SetWindowText() ) in red. This is actually pretty simple: in your main window procedure, you look for the message WM_CTLCOLORSTATIC (which gets sent to the parent window of the control), and when you receive it:
;*****************************
; WM_CTLCOLORSTATIC handler
;*****************************
; Look for handles of controls where we want red text.
; lParam = handle of control that sent message:
doRed: MOV EAX, lParam
CMP EAX, SigRawOutHandle
JE @F
CMP EAX, ExpRawOutHandle
JE @F
CMP EAX, SigDecOutHandle
JNE common_exit
@@: INVOKE SetTextColor, wParam, $colorRed ;wParam = HDC.
INVOKE SetBkColor, wParam, BkColor
; Return with background brush:
MOV EAX, BkBrush
RET
Windows conveniently sends you two handles with that message, in lParam and wParam: one is the handle of the control sending the message, and the other is a handle to its device context (HDC) which you can use to manipulate the display colors.
Anyhow, hope some of this might be of help to someone out there.