SIMULATING THE USE OF THE CARRY FLAG UNDER De SMET C ---------------------------------------------------- It is often said that 'C' is very close to assembly language in style, although much more frugal in its use of statements and hence easier to maintain and to de-bug. Where it may miss out in comparison to assembly language is that C functions normally only return one value, be it a 'char', an 'int', a 'long' and so on whereas assembly language provides (in say the case of the 8088/8086 family of chips) for return values in more than one of the AX, BX, CX and DX registers together with information in the setting of some of the flags. The assembly language type of return values can easily be simulated in C (in this case De Smet C) when the functions are written in assembler (ASM88 language) and converted into 'O' files so that you can include them in the final run-time programme with the 'bind' command. As an example of the type of code required consider MS-DOS 2.x (and the later versions) where there are a great many functions where the carry flag is set to show that an error has occurred, then the error code itself is returned in the AX register. In the normal coding of these functions in the library included with the C compiler you just get an 'int' return value (i.e. the return value in the AX register) and you don't have any way of testing whether the carry flag is set, or not set. Whilst in most cases the return value can generally be interpreted correctly as an error (when there has been an error) the more elegant way of dealing with the problem is to test for the carry flag being set, just as you would in an assembly language function, and then examine the error value. A convenient way of doing this is to use the available 'extern' register declarations which are - extern unsigned _rax, _rbx, _rcx, _rdx, _rsi, _rdi, _res, _rds; extern char _carryf, _zerof; and which De Smet C has provided for use with its "_DOINT" function. If you do this then these values are always available for your use when you need them later and you can use any of the functions on this disk which use the De Smet '_carryf' character variable. To illustrate how the technique works and to show how the code needs to be written for the ASM88 language I have chosen the routine "FUN3CH.A" which is the "Create Handle' function of INT 21H. There are a couple of things to keep in mind when studing the code which follows. The first is that De Smet C pushes the variables (used in the function) onto the stack starting from the rightmost variable. i.e. they are in the reverse order to the way you normally write them but, because the stack is a "last-on, first-off" device then it means you can extract the information from the stack in the same order as you wrote it in the function itself. The second thing is that you don't actually disturb the stack itself. You access the information there using the Base Pointer ('bp') and then restore the Base Pointer before you exit the routine. Thirdly you pass the address of the _carryf variable to the function, not the variable itself. In C terminology you pass a pointer to _carryf not _carryf itself. The coding for FUN3CH.A now follows. The additional comments I have made here (not in the actual coding) are marked with '/*' and '*/' just as they would be in a C function. --------------------------------------------------------------------- ASM88 FILE: FUN3CH.A Create Handle ---------- WRITTEN: 25/10/87 ------- PURPOSE: This is one of a series of files which take ------- advantage of INT 21H functions under MS-DOS. In each case the error situation is marked by the carry flag being set. We use the De Smet external variable '_carryf' to see whether the carry is set on return from the function. If so, the error code can be used to obtain information about the specific error. USAGE: int FUN3CH(name, attr, &_carryf) ----- char *name; (ASCIIZ string) int attr; char *_carryf; DEPENDENCIES: De Smet C V 2.44+ ------------ Copyright 1987 - Cogar Computer Services Pty. Ltd --------------------------------------------------------------------- /* First declare the name of the routine under the heading 'CSEG'. Note the underscore needed at the end of the name. */ CSEG PUBLIC FUN3CH_ /* The actual coding of the function follows. Note the technique to save the setting of the base pointer. For the purposes of these programmes it is not necessary to make ES common with DS, but this is generally useful in ASM88 programmes. */ FUN3CH_: push bp ; normal De Smet C start mov bp,sp ; point to the stack mov ax,ds ; and make ES common with DS mov es,ax ---------------------------------------------------------------------- The unique programme follows. ---------------------------------------------------------------------- /* Take the values we need off the stack. Note the first value is located at 'bp+4' because the return address is pushed first and then 'bp' was pushed by us as we started the programme. The values are placed into the three registers as required by MS-DOS so that INT 21H can be used. */ mov dx,[bp+4] ; put address of file name into DX mov cx,[bp+6] ; put the file attribute into CX mov ah,3ch ; put the Function No. into AH int 21h /* Now test to see whether the carry flag is set, which will tell us that an error has occurred. If there is an error we use a conditional jump to the code to handle this situation. */ jc FUN3CH_ERROR /* If there is no error then we just jump over the error coding to the normal return routine. */ jmp FUN3CH_QUIT /* If there has been an error then we get the address of the (external) carry flag variable and send the value of one to this address. This is an example of the coding which can be used to send a value to any external variable which has previously had its address passed to the function. */ FUN3CH_ERROR: mov si,[bp+8] ; put address of '_carryf' variable into SI mov byte [si],1 ; and then return with _carryf = 1 /* From this point the error routine just 'falls through' to the normal programme termination where both the starting stack conditions are restored and the starting Base Pointer is restored. */ ---------------------------------------------------------------------- Normal programme termination. ---------------------------------------------------------------------- FUN3CH_QUIT: pop bp ; restore starting conditions ret ---------------------------------------------------------------------- As a side-benefit this technique allows the use of error-trapping so that an explanatory message can be printed to the screen explaining the nature of the error. The file "ERRLIST.C" is included under the sub-directory "ERRORS" to show the full list of errors which have been included in the function - PRINTERR which is also to be found in sub-directory ERRORS. If you wish to use this facility in a programme just "bind" in PRINTERR to your programme and then use the construct - if(_carryf) printerr(error_return); where "error_return" is the value returned by the specific function you have just used in the programme. If you find some (or all) of these functions useful in your normal C programming then you should use the 'LIB88' library creator to put them into an 'xxxx.S' file which can then be used to bind in the required functions to the final run-time programme. The code is freely available for personal use although I would appreciate being acknowledged as the person who wrote the functions. I have tested a lot of the functions in my own programmes, but not all of them as I don't have any networking facilities. They all appear to work as they should and as described in the 'Microsoft MS-DOS Programmer's Reference' manual, but you can never be sure until they are given a thorough work-out by as many persons as possible. Please send any correspondence and/or bug reports to - Phil E. Cogar, P.O. Box 364, Narrabeen, N.S.W. AUSTRALIA 2101 ------------------------