Silly explorations in assembly part 2

I continue to explore assembly language and how to call functions from C. This time we travel back in time to the mid 80s and leave the 64-bit arena that I described here: https://matspetterss.wordpress.com/2023/01/02/silly-explorations-in-assembly-part-1/.

OS/2 1.3 – 16-bit

I start with OS/2 1.3 that was released somewhere in 1990. It’s a 16-bit operating system, so it differs a lot from the 64-bit operating systems. This is how it looks.

I reuse the C program from earlier post, and in order to recompile it I used Microsoft C 5.1, which has support for OS/2, and IBM MASM/2 for assembling the assembly code.

The helloint.asm file

It’s not really a big difference in comparison to the 64-bit masm version. What (mostly) differs is the way parameters are passed. Assuming that the small memory model is used, the first parameter is found in [sp+4], and the next in [sp+6] and so on.

PUBLIC _helloint
PUBLIC _helloints

_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
_TEXT	ENDS
_DATA	SEGMENT  WORD PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT  WORD PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT  WORD PUBLIC 'BSS'
_BSS	ENDS
DGROUP	GROUP	CONST,	_BSS,	_DATA
	ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

_TEXT SEGMENT

_helloint PROC
      push	bp
      mov   bp, sp

      mov	ax,[bp+4]	;a1

      and   ax, 1
      jz    even16
      mov   ax, 1
      jmp   finish16
even16:
     mov  ax, 0
finish16:
      mov	sp,bp
      pop	bp
      ret
_helloint ENDP


_helloints   PROC
      push	bp
      mov	bp,sp

      mov	ax,[bp+4]	;a1
      add	ax,[bp+6]	;a2
      add	ax,[bp+8]    ; ...
      add	ax,[bp+10]
      add	ax,[bp+12]
      add	ax,[bp+14]
      add	ax,[bp+16]
      add	ax,[bp+18]
      add	ax,[bp+20]

      pop	bp
      ret
_helloints ENDP

_TEXT ENDS

END
Compiling, assembling and linking

The -Dhelloos216 is used in the C program to print out a Hello World message, expanding the message as follows:

#ifdef helloos216
#define hellostr "Hello, world! OS/2 16-bit :-)"
#endif

Linking is a straight-forward task.

And here you see the Hello World-message as well as the output from running the program.

OS/2 2.11 – 32-bit

Let’s now explore the 32-bit version of OS/2. I picked the 2.11 version that was released in 1994. The first 32-bit version (2.0) was released in 1992.

This is how it looks.

The helloint.asm file

The 32-bit version introduces a flat memory model instead of the segmented 16-bit model. The flat model is easier to use from a programming perspective.

The first three parameters are passed in the EAX, EDX and ECX registers, and the following are passed on the stack (ESP+08H, ESP+0CH and so on). Each integer now occupies 32 bits, compared to 16 bits as in the previous example.

.386P
.MODEL flat
CODE32	SEGMENT DWORD USE32 PUBLIC 'CODE'
CODE32	ENDS
DATA32	SEGMENT DWORD USE32 PUBLIC 'DATA'
DATA32	ENDS
CONST32	SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST32	ENDS
BSS32	SEGMENT DWORD USE32 PUBLIC 'BSS'
BSS32	ENDS
DGROUP	GROUP CONST32, BSS32, DATA32
	ASSUME	CS:FLAT, DS:FLAT, SS:FLAT, ES:FLAT

.STACK 4096

CODE32	SEGMENT
	ALIGN 04H

public helloint
public helloints

helloint PROC
	PUSH	EBP
	MOV	EBP,ESP
	SUB	ESP,04H
	MOV	[EBP+08H],EAX;	a1
	MOV	[EBP+0cH],EDX;	a2
	MOV	[EBP+010H],ECX;	a3

      mov	eax, [ebp+8h]	;a1

      and   eax, 1
      jz    even32
      mov   eax, 1
      jmp   finish32
even32:
     mov  eax, 0
finish32:
      mov	esp,ebp
      pop	ebp
      ret
helloint ENDP


helloints   PROC
	PUSH	EBP
	MOV	EBP,ESP
	SUB	ESP,04H

	MOV	[EBP+08H],EAX;	a1
	MOV	[EBP+0cH],EDX;	a2
	MOV	[EBP+010H],ECX;	a3

      mov	eax,[ebp+08H]	;a1
      add	eax,[ebp+0CH]	;a2
      add	eax,[ebp+10H]    ;a3
      add	eax,[ebp+14H]
      add	eax,[ebp+18H]
      add	eax,[ebp+1CH]
      add	eax,[ebp+20H]
      add	eax,[ebp+24H]
      add	eax,[ebp+28H]
      mov	esp,ebp

      pop	ebp
	MOV	[EBP-04H],EAX
      ret
helloints ENDP

CODE32 ENDS
END
Compiling, assembling and linking

I used the IBM C/C++ Tools version 2.0 to compile the C code.

I had a hard time to find a 32-bit macro assembler. Finally I understood that the Microsoft MASM version 6.0 had support for 32-bit OS/2.

Finally linking the object modules together using link386.

Here you have the output from the 32-bit OS/2 world 🙂

DOS (3.3) – 16-bit

If you look at the details, you will actually see that this source is very similar to the 16-bit OS/2. I made some adjustments in the code because I wanted to test small and large memory models, because of the differences in the locations for the parameters. Otherwise it is the same.

The helloint.asm file

I introduce a constant in order to conditionally assemble the source depending on the memory model (hellodos_l).

PUBLIC _helloint
PUBLIC _helloints
hellodos_l	equ	1

_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
_TEXT	ENDS
_DATA	SEGMENT  WORD PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT  WORD PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT  WORD PUBLIC 'BSS'
_BSS	ENDS
DGROUP	GROUP	CONST,	_BSS,	_DATA
	ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP

_DATA SEGMENT
_DATA ENDS

_TEXT SEGMENT

ifdef hellodos_l
_helloint PROC FAR
else
_helloint PROC NEAR
endif

      push	bp
      mov   bp, sp

ifdef hellodos_l
      mov	ax,[bp+6]	;a1
else
      mov	ax,[bp+4]	;a1
endif
      and   ax, 1
      jz    even16
      mov   ax, 1
      jmp   finish16
even16:
     mov  ax, 0
finish16:
      mov	sp,bp
      pop	bp

      ret

_helloint ENDP


ifdef hellodos_l
_helloints PROC FAR
else
_helloints PROC NEAR
endif

      push	bp
      mov	bp,sp

ifdef hellodos_l
      mov	ax,[bp+6]	;a1
else
      mov	ax,[bp+4]	;a1
      add	ax,[bp+6]	;a2
endif
      add	ax,[bp+8]
      add	ax,[bp+10]
      add	ax,[bp+12]
      add	ax,[bp+14]
      add	ax,[bp+16]
      add	ax,[bp+18]
      add	ax,[bp+20]
ifdef hellodos_l
      add	ax,[bp+22]
endif
      pop	bp
      ret
_helloints ENDP

_TEXT ENDS

END
Compiling, assembling and linking

For DOS I use the old C compiler from 1985 and the IBM Macro assembler from 1981. It feels ancient to run this code 🙂

I use a more modern linker for linking the modules. It is actually from OS/2, but also runs on DOS.

Running it, compiled and linked with the large memory model.

And with the small memory model. I’m happy to see the output is the same and the calculations are correct.

The helloint.asm file (nasm version)

Having been so deep into all this old stuff, I wondered if nasm could be used in DOS to produce the object module. It has a different syntax than masm, so here is the code for that. Here is the version for the small memory model, that I later linked with the C code.

global _helloint
global _helloints

section ._text

_helloint:
      push	bp
      mov   bp, sp

      mov	ax,[bp+4]	;a1
      and   ax, 1
      jz    even16
      mov   ax, 1
      jmp   finish16
even16:
     mov  ax, 0
finish16:
      mov	sp,bp
      pop	bp
      ret      

_helloints:
      push	bp
      mov	bp,sp
   
   mov	ax,[bp+4]
   add	ax, [bp+6]	
      add	ax,[bp+8]
      add	ax,[bp+10]
      add	ax,[bp+12]
      add	ax,[bp+14]
      add	ax,[bp+16]
      add	ax,[bp+18]
      add	ax,[bp+20]
;      add	ax,[bp+22]	
      pop	bp

      ret

Linking it gave warning messages, but the actual executable worked fine. I never understood how to solve these warnings. I save that task for another day.

To summarize

It was fun to explore these differences, but what was even more fun was the actual journey back in time. I used all these tools at the time, and it was a true pleasure to get them running again.

I used Linux tools to actually write the code, because I’m just to more modern editors nowadays. To get a setup that worked for my workflow was quite interesting as well.

Running dosbox on Linux helped me a lot with the DOS versions.

I had some concerns with how carriage return/line feeds were handled in the DOS environment. But there are tools available for that.

If you have read this far, I hope that you enjoyed it.


Posted

in

,

by

Comments

Leave a comment

Design a site like this with WordPress.com
Get started