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 -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
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 :-)