Hello World in ASM86 and NASM

Peter Haagerup, April 2014

Today I went through a lot of Digital Research documentation for Concurrent CP/M-86 and related programming. After all that reading, I still did not know how to make a simple "Hello World" program that will run on the RC75x microcomputers. After some googling on x86 assembly language I finally did a working program in ASM86 syntax. In this article I present the code and how to make it run. Later in the article, I will present the NASM equivalent that Hampa Hug (creator of the PCE Emulator) e-mailed me a while ago. This version is far more complicated, but it can create a working CMD executable directly on a modern PC — which I find very exciting.

The program

This "Hello World" example makes use of two BDOS system calls. These are 9 (C_WRITESTR) and 0 (C_TERMCPM).


; Hello World ASM86 example for CCP/M-86
; Peter Haagerup, 2014-04-16 (rc700.dk)

; This example will print the string "Hello World" on the screen.

    CSEG                           ; CODE segment
    mov    cl, 09h                 ; BDOS function C_WRITESTR
    lea    dx,msg                  ; Load address of the string "msg"
    int    224                     ; Interrupt E0h
    
    mov    cl, 00h                 ; BDOS function P_TERMCPM
    int    224                     ; Interrupt E0h


    DSEG                           ; DATA segment
    org    100h                    ; Code offset 100h
    msg    db      'Hello World!$' ; Define the string "msg"
    end                            ; End of DATA segment

As far as I understand (from the not so beginner-friendly official CP/M-86 documentation), a BDOS function is called by placing the function number in the CX register and then make an interrupt 224 (hex E0). If the function takes "arguments" they have to be supplied in the appropiate register(s) before the interrupt. In the case of C_WRITESTR, this is done by placing the address of the string msg in the DX register. In the NASM code (as seen later in this article) this is done with the MOV instruction. In the ASM86 syntax, this has to be done with the LEA instruction (Load Effective Address). That took a while for me to figure out.

The data segment contains org directive and the string definition. The org directive tells the assembler where in the memory to put the data. In this case, the code segment is very small, so location 100h will be fine. Do not forget to terminate the string with a dollar symbol! If you do forget, strange things will happen.

How to assemble the program

First, save the code in a file named HELLO.A86 and transfer it to the RC75x or any other C/PM-86 machine. Remember to convert the line endings and terminate the file in the well known CP/M style. If you use cpmtools to copy this file to a (virtual) floppy disk, you can supply the -t option to cpmcp — this will automatically fix line endings and termination (text files only).

Now boot up the machine with a bootable floppy disk. Make sure that you have ASM86.CMD and GENCMD.CMD around (these are available on the system disks for PICCOLINE found in the Software section on this site). Then verify that HELLO.A86 is fine. I used the TYPE command to print out the file. As you can see in the Figure 1 (screenshot from the RC759 emulator), line endings and termination looks just fine.


Figure 1: HELLO.A86.

Now we assemble the code and afterwards generate a CMD executable by typing these command:


asm86 hello
gencmd hello

As seen in Figure 2, some output is produced, both on the screen and in files. Everything went fine and we can finally test our program. It works!


Figure 2: Assembling and CMD generation.

Note that besides HELLO.CMD generated by GENCMD three other files are generated by ASM86. These are a .LST, .H86 and .SYM file. The LST-file is just the original code with some additional information. The H86-file is containing ASCII hexadecimal values of the machine code. This is the file that GENCMD converts to the CMD-file. The SYM-file contains a view of variables and other things that is probably more usable with larger programs.


Figure 3: Additional files generated by ASM86.

The NASM equivalent

Hampa Hug (the creator of the PCE Emulator and the RC759 Emulator) e-mailed me a NASM program a while ago, that is quite similar to the program above. NASM is another assembler and can be used to generate 16-bit x86 code, just like ASM86. Hampa even made some additional includes (not shown here, but downloadable) that does something remarkable — the output of the assembling process is not only a LST-file but also a CMD-file! And what is even better: The CMD file actually works and can be executed on the RC759 (probably RC75x and other (C)CP/M-86 machines as well). If you like writing assembly code, you now have a very fast and relatively easy workflow to create working applications for CP/M-86!

The source file HELLO.ASM is shown here:


; hello.asm

%include "cpm-cmd.inc"

cmd_start

section	.text

	times	256 db 0

start:
	mov	ax, cs

	cli
	mov	ss, ax
	mov	sp, stack_top
	sti

	mov	cl, 0x09		; C_WRITESTR
	mov	dx, msg
	int	0xe0

	mov	cl, 0x00		; P_TERMCPM
	int	0xe0


section	.data

msg	db	"Hello World", 0x0d, 0x0a, "$"


section	.bss

	resb	($ - $$) & 1

stack_base:
	resb	1024
stack_top:

cmd_end

Note that the file cpm-cmd.inc is included. This is what makes the CMD-file. The code looks quite a bit more complicated than HELLO.A86. This code includes the setup of the stack and stack pointer. Apart from that and the included file, not to mention the syntax difference, the code is almost identical. However, the string is concatenated with a carriage return and a line feed before termination.

To compile Hampa's version, download hello.tgz, extract it and type make in a terminal (Unix/Unix-like system with GNU Make and NASM installed is required). Then use cpmtools to copy hello.cmd to a (virtual) floppy disk and boot up the machine/emulator.


Figure 4: The NASM version of the program (drive B) compared to the ASM86 version (drive A).

As seen in Figure 4, the NASM version makes a new line before termination as expected. The coolest thing about Hampa's version is that it actually does not need GENCMD and it shows that it is in fact possible to create working executables in different languages — languages that are not usually available in the CP/M-86 world, such as NASM.

Future work

For most people, including myself, programming in the assembly language in any form is not very comfortable. In my opinion, the only way to port modern software to the CP/M-86 system is to make it possible to use more modern languages such as C and C++. I would love to compile a C++ version of "Hello World" directly to a CMD-file. This might be a dream that will never come true, though I am sure that it can be done. If only I had the knowledge and the time it takes... until then, I might post some more assembly programming examples. Stay tuned :-)

Downloads