Monday, March 17, 2014

Using the CP/M Assembler

I'll continue giving more details on the programs I demonstrated in the YouTube video The Briel Altair 8800 Kit, Part 5: Advanced Topics.

This time, the CP/M Assembler.

To follow the demos in the video, boot up a CP/M 2.2 image that has the assembler (ASM.COM), loader (LOAD.COM), and optionally the DDT debugger (DDT.COM). The Briel CPM22.DSK image on the CD will work.

If you use CP/M 3, there is a more sophisticated macro assembler called MAC.COM. I have not tried it with these examples.

Create a file HELLO.ASM with the following source code and copy it to your SD card. Use TREAD to copy it to the CP/M disk (or enter it locally on CP/M if you have a text editor such as ED or WordStar).

; This is an example of the "Hello World" program.
; Uses 8080 assembler mnemonics.
ORG 100h ; cpm programs start address.
JMP START ; go to program start.

; Variable storage space
MsgStr: DB 13,10,'Hello world.',13,10,0
Stack1: DW 0 ; place to save old stack.
Sbot: DS 32 ; temp stack for us to use.

; Constants
STOP: EQU $-1 ; top of our stack.
BDOS: EQU 5 ; address of BDOS entry.

; Start of code segment
START: LXI H, 0 ; HL = 0.
DAD SP ; HL = SP.
SHLD Stack1 ; save original stack.
LXI H, STOP ; HL = address of new stack.
SPHL ; stack pointer = our stack.
LXI H, MsgStr ; HL = address of string.
LOOP1: MOV A, M ; read string char.
ORA A ; set cpu flags.
JZ EXIT ; if char = 0 done.
MOV E, A ; E = char to send.
MVI C, 2 ; we want BDOS func 2.
PUSH H ; save HL register.
CALL BDOS ; call BDOS function.
POP H ; restore HL register
INX H ; point to next char.
JMP LOOP1 ; do next char.

; Exit and return code
EXIT: LHLD Stack1 ; HL = entry stack address.
SPHL ; SP = value on entry.
RET ; return control back to CPM.
END

Since I recorded the video, I found an even simpler hello world program which you can use instead if you like. The source code is below. I saved it as HELLO1.ASM and used it in the examples that follow.

        org 100h
bdos    equ    0005h    ; BDOS entry point
start:  mvi    c,9      ; BDOS function: output string
        lxi    d,msg$   ; address of msg
        call   bdos
        ret             ; return to CCP
msg$:   db    'Hello, world!$'
        end

Here is an example session assembling, linking, and running it, and disassembling it with DDT. Commands typed by the user are in bold. Note that in the video, when I demonstrated DDT, I did not correctly load the file, so the disassembly in the video was incorrect. The example below is correct. Note that this session was run on drive B so I needed to specify the path to the tools on drive A.

B>a:asm hello1

CP/M ASSEMBLER - VER 2.0
0117
000H USE FACTOR
END OF ASSEMBLY

B>a:load hello1

FIRST ADDRESS 0100
LAST  ADDRESS 0116
BYTES READ    0017
RECORDS WRITTEN 01

B>type hello1.hex
:100100000E09110901CD0500C948656C6C6F2C20E2
:07011000776F726C6421247B
:0000000000

B>type hello1.prn

 0100                   org 100h
 0005 =         bdos    equ    0005h    ; BDOS entry point
 0100 0E09      start:  mvi    c,9      ; BDOS function: output string
 0102 110901            lxi    d,msg$   ; address of msg
 0105 CD0500            call   bdos
 0108 C9                ret             ; return to CCP
 0109 48656C6C6Fmsg$:   db    'Hello, world!$'
 0117                   end

B>a:ddt hello1.com

DDT VERS 2.2
NEXT  PC
0180 0100
-l
  0100  MVI  C,09
  0102  LXI  D,0109
  0105  CALL 0005
  0108  RET  
  0109  MOV  C,B
  010A  MOV  H,L
  010B  MOV  L,H
  010C  MOV  L,H
  010D  MOV  L,A
  010E  INR  L
  010F  ??=  20
-^C
B>

Note that the assembler is for the Intel 8080. If you find CP/M programs for the Zilog Z80 and try to build them, they will not assemblem as it uses different mnemonics. They also won't run on the
Briel Altair 8800 as it only emulates Intel 8080 instructions.

The source for the "hello world" 8080 assembler program I listed above, as well as more information on ASM, can be found here.

No comments: