    ;; This example shows how to access the firmware from within CP/M 2.2 and CP/M+.
    ;;
    ;; CP/M 2.2 and CP/M+ use a different address to execute firmware functions
    ;; but the parameters and method of calling are the same.
    ;;
    ;; Detect the version of CP/M which is in use and setup the correct firmware
    ;; jump. If a incorrect version of CP/M has been detected then just quit.
    ;;
    ;;---------------------------------------------------------

    org &100							;; origin for C/PM .COM programs
    nolist
    write"test3.com"

    .enter_firmware equ &be9b
    .scr_set_mode equ &bc0e
    .txt_output equ &bb5a
    .bdos equ 5

    ;;---------------------------------------------------------
    ;; get version of CP/M

    ld c,&0c							;; BDOS function get CP/M version
    call bdos
    ld a,l								;; CP/M Version
    cp &22								;; CP/M 2.2?
    jr nz,cpmtst1

    ;;-----------------------------
    ;; C/PM 2.2 detected
    ;;
    ;; initialise firm_jump with
    ;; the appropiate value

    ld hl,enter_firmware
    ld (firm_jump+1),hl
    jp start

    .cpmtst1
    cp &31								;; C/PM + (C/PM 3.1)?
    jr nz,cpmtst2

    ;;------------------------------
    ;; C/PM+ detected
    ;;
    ;; initialise firm_jump with
    ;; the appropiate value

    ld hl,(&0001)
    ld de,&0057
    add hl,de
    ld (firm_jump+1),hl

    jp start

    .cpmtst2
    ret

    ;;---------------------------------------------------------
    ;; the main program

    .start
    ;;----------------------------------
    ;; change the display mode to mode 1
    ld a,1
    call firm_jump
    defw scr_set_mode

    ;;----------------------------------
    ;; display a message on the screen
    ld hl,test_message
    call display_message

    ;;----------------------------------
    ;; quit back to command-line
    ret

    ;;---------------------------------------------------------
    ;; HL = pointer to null terminated message
    .display_message
    ld a,(hl)				;; get ASCII character
    inc hl					;; increment pointer for next character
    or a					;; end of message marker (0)?
    ret z					;; quit if end of message marker found.

    call display_char		;; send character to console output
    jp display_message		;; loop for next char

    ;;---------------------------------------------------------
    .display_char
    call firm_jump
    defw txt_output			;; firmware function: TXT OUTPUT
    ret

    ;;---------------------------------------------------------
    ;; execute a firmware function
    .firm_jump
    jp 0

    ;;---------------------------------------------------------
    .test_message
    defb "Hello World!",0