글

라벨이 codes인 게시물 표시

OS/2 codes: NASM and 32-bit FLAT sections/segments

NASM is a widely used assembler, and the only modern assembler for OS/2. Almost all the open-source projects using assembly codes are using NASM. Because of this, porting them to OS/2 is much easier. BTW, NASM manual says that FLAT property is necessary to declare OS/2 32-bit sections/segments for 32-bit FLAT memory model. Unfortunately, however, NASM manual seems to be wrong because FLAT is not enough nor necessary for OS/2 32-bit FLAT memory model. Here is the correct way to declare 32-bit FLAT section/segment on OS/2. In general, OS/2 32-bit section/segment is declared like this:  section TEXT32 public align=16 use32 class=CODE This declares 32-bit( use32 ) CODE segment( class=CODE ) whose name is TEXT32 with 16-bytes aligned( align=16 ). And this segment will be concatenated with segments with the same segment name( public ) at linke time. For details, see NASM docs( https://www.nasm.us/xdoc/2.15.05/html/nasmdoc8.html#section-8.4.1 ). Here, 'FLAT' property is not needed. R...

OS/2 codes: How to get size of free physical memory

OS/2 provides APIs to get size of being interested in system memory. For example, DosQuerySysInfo() with QSV_TOTPHYSMEM for total size of physical memory, QSV_TOTRESMEM for total size of system-resident memory, and QSV_TOTAVAILMEM for total size of total available memory for all processes. However, those indice do not tell us about free physical memory. Actually, any 32-bits APIs do not provide such information at all. Instead, you can use 16-bits APIs. That is, DosMemAvail() . Here is the prototype of DosMemAvail(): APIRET16 APIENTRY16 Dos16MemAvail( PULONG pulAvailMem ) For details, see https://komh.github.io/os2books/prcp/080_L2_DosMemAvail.html . VisualAge C++ and Watcom can use this prototype directly. However, unfortunately, gcc does not provide features to call 16-bits functions directly. Instead, you should use thunking. Fortunately, kLIBC provides macros for thunking. You can call DosMemAvail() on gcc/kLIBC like this: Colored By Color Scripter ™ 1 2 3 4 5 6 7 8 9 USHORT...

OS/2 codes: workaround for a bug of _path2() of kLIBC v0.6.6

Since kLIBC v0.6.6, new functions for search-in-path are introduced. They are _path2() , _searchenv2() , _searchenv2_value() , _searchenv2_has_suffix() and _searchenv2_one_file() . Out of these, unfortunately, _path2() has a bug which is fixed already but not released yet. For details, see the ticket . Because of this, functions using _path2() such as spawn[lv]p[e]() and exec[lv]p[e]() have the same bug. Until the fixed new libc is released, there is a way to workaround for the bug. The bug occurs if and only if EMXPATH env var is not set and a directory entry with the same name as the executable exists. So setting empty EMXPATH would be able to avoid the bug unless EMXPATH is set. Put the following codes into startup codes. Colored By Color Scripter ™ 1 2 if  (!getenv( "EMXPATH" ))     putenv( "EMXPATH=" );

OS/2 codes: compiler keywords exporting a symbol from a DLL without .def

The most basic way to export a symbol from a DLL is to list entries below EXPORTS keyword in .DEF file. However, this works for Visual Age C++(VAC) and gcc, but not for Open Watcom C/C++(OW). OW uses its own linker script, .lnk file, and EXPORT keyword not EXPORTS. Because of this, if you want to write DLLs buildable by the above three compilers, you should maintain at least two files of .def for VAC and gcc, and .lnk for OW. This increases the complexity of maintence. As well as, whenever symbols are exported newly, they should be added to .def and .lnk themselves. This is very annoying. Instead, it would be very convenient if exporting symbols in the source itself. All three compilers provide keywords for this. 1. Visual Age C++ VAC provides _Export keyword and #pragma export directive. Especially, _Export may be used in in-line style. For example, void _Export dll_entry( void ); will export dll_entry() as a proper name according to its calling convention and linkage ...

OS/2 codes: How to control CD-ROM drives

OS/2 provides ioctl commands to control CD-ROM drives and discs. And some commands have been added with new OS2CDROM.DMD. In this article, let's research the ways to control CD-ROM drives and to utilize new commands. 1. Opening or Closing a Tray The most strange thing of OS/2 ioctl commands are CDROMDISK_CLOSETRAY . It's not possible to issue the command. Because a handle for a disc is required to issue, but there is no way to open a handle for the opened drives. This is applied to CDROMDISK_EJECTDISK . It's not possible to issue the command if a disc is not in a drive. To open and/or to close a tray, another way should be used. OS/2 provides general ioctl commands to eject and to load a media for a removable media . Ejecting is used to open a tray. Loading is used to close a tray. These commands can be issued without a handle. Therefore, it's possible to open and to close a tray even if a disc is not in the drive. Codes are like this: Colored By Color Scri...

OS/2 codes: How to manipulate FAT32 paritions

On OS/2, in general, it's possible to access to any partitions in DASD mode. This is true for FAT32 partitions. Unfortunately, however, accessing in DASD mode may not work on FAT32 partitions, especially writing to them. Instead, FAT32.IFS provides special ioctl commands. Let's see the way accessing to FAT32 partitions. 1. Accessing FAT32 partitions in DASD mode Accessing in DASD mode is the general ways on OS/2. Unfortunately, however, FAT32.IFS does not allow to access the areas beyond 2GB by default. This is true for HPFS.IFS. To overcome this problem, FAT32.IFS provides a special mode, SECTOR IO mode, like HPFS.IFS. If the sector io mode is enabled, accessing in DASD mode is performed in sectors not in bytes. To enable sector io mode, you should call DosFSCtl() , and pass 0xDEADFACE as a parameter list like this: Colored By Color Scripter ™ 1 2 3 4 5 6 7 ULONG cbData     = 0; ULONG ulDeadFace = 0xDEADFACE; ULON...

OS/2 codes: How to get information about CPUs

OS/2 provides various APIs to get information of CPU, from number of CPUs to CPU time snapshot. 1. Number of CPUs 1.1 DosQuerySysInfo( QSV_NUMPROCESSORS ) DosQuerySysInfo() returns the requested system information. If passing QSV_NUMPROCESSORS , number of CPUs are returned to the buffer. Look: DosQuerySysInfo( QSV_NUMPROCESSORS, QSV_NUMPROCESSORS,                  &ulCpus, sizeof( ulCpus )) 1.2 DosGetProcessorStatus() DosGetProcessorStatus() returns the status of the given CPU ID starting from 1. If CPU ID is valid, that is, ID-th CPU exists, it returns PROC_ONLINE or PROC_OFFLINE according to the status of that CPU. PROC_ONLINE means that the given CPU is available for running work. PROC_OFFLINE means that the given CPU is not available. Whereas the given CPU does not exist, it returns an error, ERROR_INVALID_PARAMETER . After all, number of CPUs can be caculated if counting from 1 unt...

OS/2 codes: How to check if IFS was loaded

IFS stands for Installable File System. With IFS, OS/2 can support various file systems even if they are not supported by default. What should we do to check if a specific IFS was loaded ? First of all, we can iterate all drives from A to Z, and check the attached IFS to them. However, this method cannot check IFS which is loaded but not attached yet. In addition, same IFS may be attached to the different drives. This causes extra cost when programming. To solve the above unconveniences is to check IFS directly. How ? DosFSCtl() provides such functions. DosFSCtl() provides the communication between an application and a specific IFS. If the given IFS was not loaded, it returns ERROR_INVALID_FSD_NAME , otherwise any other error codes. Using this, it's possible to check if a specific IFS was loaded. However, note that the IFS name passed to DosFSCtl() is neither the filename of the IFS nor the file-system name in the boot sector. It's the exported name by IFS. For examp...

OS/2 codes: How to emulate COPY-ON-WRITE #4 Epilogue

So far, we've researched the basic strucutre of the implementation of the copy-on-write feature. As well as, we've looked into the ways to enable copy-on-write in all threads including a main thread. Nevertheless, this approach has some limitations. Copy-on-write will not work if it is used in a thread started by custom codes using DosCreateThread() not _beginthread() it is used in an alien thread created in DLLs not linked with copy-on-write module source memory blocks are allocated malloc() not DosAllocMem() In the above first two cases, installing/uninstalling an exception handler in the context using copy-on-write manually is needed. In case of malloc(), it may be succeeded, but it's not recommended. Failure possibility is higher. Finally, copy-on-write not supported by Kernel is not optimal and may not be useful due to its efficiency. However, the fact itself that implementation of copy-on-write is possible is meaningful even if it is not actually usefu...

OS/2 codes: How to emulate COPY-ON-WRITE #3 Install exception handler

To activate the exception handler, it's needed to install it. By the way, the handler works only on the thread on which it is installed. After, we should install the handler on every threads. Let's see the way to install a handler first: EXCEPTIONREGISTRATIONRECORD regRec = { 0 }; /* Register an exception handler for SIGSEGV */ regRec.ExceptionHandler = ( ERR )sigsegv; DosSetExceptionHandler( &regRec ); EXCEPTIONREGISTRATIONRECORD is a type to register handler. Generally, it should be initialized to 0 except ExceptionHandler field. ExceptionHandler should be set to the handler to be registered. Finally, register the handler with DosSetExceptionHandler() . Here, note that EXCEPTIONREGISTRATIONRECORD variables should be placed in the stack . Uninstalling the installed handler is simple: /* Deregister an exception handler */ DosUnsetExceptionHandler( &regRec ); regRec is the variable which was used when installing the handler. DosUnsetExceptionHandler() ...

OS/2 codes: How to emulate COPY-ON-WRITE #2 Exception handler

In the previous article, we looked into a basic structure of a main function. In that, the last question was how to catch write access to the registered memory blocks. Here is the answer. It's the exception. We removed WRITE permission of the memory blocks. Because of this, writing to those blocks causes an exception, so called SIGSEGV. To catch such exceptions, we should implement exception handlers. Exception handler is like this: ULONG _System sigsegv( PEXCEPTIONREPORTRECORD p1,                        PEXCEPTIONREGISTRATIONRECORD p2,                        PCONTEXTRECORD p3,                        PVOID pv ); Out of the above parameters, p1 is the most im...

OS/2 codes: How to emulate COPY-ON-WRITE #1 main function

A basic principle of copy-on-write is the following. Remove WRITE permission of source memory blocks Create an alias memory without WRITE permission for source memory blocks Register information for these Let's see the ways to implement the above sentences. 1. Remove WRITE permission / Alter access protections of memory blocks To do this, all you have to do is calling DosSetMem() like this: DosSetMem ( p, cb, flSrc & ~PAG_WRITE )) Where p is the pointer to the source memory block, cb is the size of the blocks, flSrc is the access protection flags of those. Then, how can we get the protection flags of the source blocks ? Use DosQueryMem(): DosQueryMem ( p, &cbSrc, &flSrc ); Where p is the pointer to the memory block, cbSrc is the size to be queried. flSrc is the place where to store access flags. 2. Create an alias memory An alias memory is an memory sharing same contents with the original memory. For details, see < DosAliasMem(), ma...

OS/2 codes: How to emulate COPY-ON-WRITE #0 Prologue

Copy-On-Write is a feature to improve copy performance. That is, memory blocks share the same contents until copy operation to those blocks really occur. By postponing copy-operations until it is needed really, it's possible to remove initial overheads of copy operation. This was needed to implement mmap(). Espeically, MAP_PRIVATE. On Linux( http://man7.org/linux/man-pages/man2/mmap.2.html ), copy-on-write feature is used. I thought it's not possible without kernel supports. However, Dave Yeo suggested DosAliasMem(). I thought it may be possible. I did implement such a feature. A series of the following articles are records of implementing copy-on-write on OS/2.

OS/2 codes: How to get a module name from a symbol

Sometimes it's needed to know the module where a symbol is. For example, it's needed to know the location of a DLL where I am. To do this, two APIs are required. One is DosQueryModFromEIP() and the other one is DosQueryModuleName(). 1. DosQueryModFromEIP() Here is the description. Purpose DosQueryModFromEIP queries a module handle and name from a given flat address. It takes a flat 32 bit address as a parameter and returns information about the module (a protect mode application currently executing) owning the storage. Syntax #define INCL_DOSMODULEMGR #include os2.h> APIRET APIENTRY DosQueryModFromEIP (HMODULE *phMod, ULONG *pObjNum, ULONG BuffLen, PCHAR pBuff, ULONG *pOffset, ULONG Address) Parameters phMod (PHMODULE) output Address of a location in which the module handle is returned. pObjNum (PULONG) output Address of a ULONG where the module object number corresponding to the Address is returned. The object is zero based. BuffLen (ULONG) i...

OS/2 codes: How to get a network interface name from an index and a description of struct ifmib

OS/2 socket call, os2_ioctl( SIOSTATIF42 ), provides the informations about network interfaces available on a machine through struct ifmib . However, struct ifmib does not have a member for a canonical network interface name such as 'lan0'. Instead of, it has an index and a description of a network interface. Fortunately, with those informations it's possible to get a canonical network interface name. An algorithm for this can be found in ODIN sources. See http://trac.netlabs.org/odin32/browser/trunk/src/iphlpapi/iphlpapi.cpp#L169   Based on the above codes, I've made an utility function to get a canonical network name from an index and a description of struct ifmib . See source: https://github.com/komh/os2codes/blob/master/network/getifname.c header: https://github.com/komh/os2codes/blob/master/network/getifname.h test: https://github.com/komh/os2codes/blob/master/network/getifname-test.c

OS/2 codes: How to get MAC address of network interfaces

MAC address is an 48-bits unique hardware address of a network adapter. OS/2 provides socket calls to retrive MAC address from network adapters. First way is to use os2_ioctl( SIOSTATIF42 ) . It returns the informations about all network interfaces available on a machine into struct ifmib . struct ifmib is based on an index of a network interface. That is, it does not provides canonical name of a network interface such as lan0. You should mangle a canonical name from a description field and an index field. For details, see the following ODIN sources. http://trac.netlabs.org/odin32/browser/trunk/src/iphlpapi/iphlpapi.cpp#L169 Second is ioctl( SIOCGIFCONF ) . It returns the information about all network interfaces available on a machine into struct ifconf . On TCP/IP v4.21 or later, struct ifconf may contain AF_LINK address storage. In this case, strcut ifconf contains a canonical name of a network interface, its index and its MAC address. Here is the implementation to re...

OS/2 codes: How to make 'char' type of gcc + kLIBC compatible with IBM Visual Age C/C++ and Open Watcom C/C++

When you compile sources for IBM Visual Age C/C++(later VAC) or Open Watcom C/C++(later OW) with gcc/g++ + kLIBC(later gcc), there is a thing you should notice. It is char type. char type is a primitivie type to hold a character on C/C++. And it is usually 1-byte size. If it holds an integer, it can be singned char or unsigned char according to implementaiton. VAC and OW define char to unsigned char by default. Its range is 0 to 255. On the other hands, gcc define char to signed char by default. Its range is -128 to 127. This difference has no problem usually. However, if bit-wise operation is used or DBCS characters manipulations are performed, potential problems may occur. For example, if you did right-bit-shift by 1 on a char varaible holding 128(10000000 in binary), you would expect 64(01000000b). But you may get 64(01000000, VAC and OW) or -64(11000000b, gcc). This is because right-bit-shift conserve MSB(sign-bit) if signed-type. Try to build and run the follo...

OS/2 codes: How to execute a child process in a specific code page

A code page is legacy and is being replaced with a unicode system. Unfortunately, however, OS/2 still use a code page. In addition, it supports maximum 2 code pages only at a time. They are called as primary code page and secondary code page, respectively. Of course, PM programs can more free from a code page, but they are also affected by a code page, so called process code page. In this ariticle, we will find how to control those code pages, especially for child process. OS/2 provides several APIs for a code page. Out of them, our concern is DosSetProcessCp() . Its systanx is #define INCL_DOSNLS #include <os2.h> ULONG cp ; /* A code page identifier. */ APIRET ulrc ; /* Return Code. */ ulrc = DosSetProcessCp(cp); Reference: http://cyberkinetica.homeunix.net/os2tk45/cp1/1726_L2H_DosSetProcessCpSynta.html DosSetProcessCp() changes the current process code page. And the important thing is the changed code page is inherited by a child process. However, th...

OS/2 codes: How to allocate in high-memory area

By default, OS/2 allocates a memory in low-memory area, which is below 512 MB. However, nowadays, more and more applications requires more and more memory. Fortunately, OS/2 provides ways to allocate in high-memory area, which is above 512 MB. In this case, applications can use much more memory limited by VIRTUALADDRESSLIMIT and physical memory + swap memory. So if possible, it would be better to try to allocate in high-memory area first. Now, let's see the ways to allocate in a high-memory. 1. DosAllocMem() with OBJ_ANY First is to use OS/2 API DosAllocMem() . DosAllocMem() provides a flag to allocate in high-memory area. It's OBJ_ANY . Following is an example. Colored By Color Scripter ™ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* highmem1.c */ #define  INCL_DOS #include  <os2.h> #include  <stdio.h> int  main(  void  ) {     PVOID p;     APIRET...

OS/2 codes: DosAliasMem(), mapping an address to another address

Many modern OSes support memory mapping. For example, mmap() family of POSIX. However OS/2 has no such APIs supporting it fully. Instead, OS/2 provides an API supporting it partially, which maps an address to another address. It's DosAliasMem() . DosAliasMem() has not been known until Toolkit 4.5. You can see the explanation about DosAliasMem()  in <OS/2 Programming Guide and Reference Addendum> Toolkit 4.5, or http://cyberkinetica.homeunix.net/os2tk45/addendum/022_L2_DosAliasMem.html . Here is a brief explanation. #define INCL_DOSMEMMGR #include <os2.h> APIRET APIENTRY DosAliasMem (PVOID pMem , ULONG cbSize , PPVOID ppAlias , ULONG flags )   Parameters pMem    input           Contains the address of the memory to be aliased. It must be on a page boundary (that is, 4K aligned), but may specify an address within a memory object. cbSize    input       ...