; From Dr. Dobb's Journal, Number 73, November 1982, pp. 6

; A letter from Oscar Goldman, extracted from
; CP/M EXCHANGE by Gene Head

; Dear Gene,
;    This scheme is based on intercepting calls to the BDOS.  The
; only visible entry point into the BDOS is the jump instruction
; at the absolute memory address of 5.  WHile it might seem a
; simple matter to change the address at 6 and 7 in order to
; effect the interception, many applications programs (e.g., DDT)
; determine memory size from the information contained there.
; Therefore, it would seem prudent to leave the content of 6 and
; 7 alone.  Fortunately, for our purpose, the instruction jumped
; to from 5 is also a jump instruction.  This observation is the
; key to the interception process.
;    Here is an outline of the modifications to be made in the
; BIOS.  (If necessary, change the identifiers to avoid
; duplication.)
;    Add the following two equates:

; MAXFUN        EQU     49      ; Last of the standard BDOS function numbers
; EXTRA EQU     ?       ; Replace the "?" with the actual number
                       ; of new BDOS calls.  The new function
                       ; numbers will start from 50.

; Toward the end of the warm boot, there is a routine called
; "gocpm".  (This name is used by Digital Research in the listing
; of the skeletal CBIOS.)  Somewhere in there will be found the
; two lines:

;       LXI     H,BDOS
;       SHLD    6

; Immediately after these, insert the following 4 lines:

;       LHLD    BDOS+1
;       SHLD    NORMAL+1
;       LXI     H,DIVERT
;       SHLD    BDOS+1

; and finish with the rest of "gocpm".
;     At a convenient point in the BIOS, add the following:

NORMAL: EQU     JMP     0       ; The 0 will be replaced during
                               ; the warm boot.
DIVERT:
       ; This point is reached with the function number in C
       ; and the BDOS call information in DE.
       MVI     A,MAXFUN
       CMP     C               ; Is it an old or new call?
       JNC     NORMAL          ; Old ones are diverted to NORMAL.
       ADI     EXTRA
       CMP     C               ; Are we exceeding the number of
                               ; allowed calls?
       JNC     OKAY            ; Continue if not.
       LXI     H,0             ; Protocol requires these registers
                               ; to be zeroed for out-of-range calls.
       RET
;
OKAY:
       ; Here we handle the new functions
       LXI     H,0             ; HL is lost.
       DAD     SP
       SHLD    SAVSTK          ; Save the stack pointer for later return,
       LXI     SP,SAVSTK       ; and set up a local stack.
       XCHG
       SHLD    NTRPAR          ; Save the entry parameters.
       LXI     D,FIN
       PUSH    D               ; A place to return to when done.
       MOV     A,c
       SBI     MAXFUN+1        ; Start counting from 0.
       ADD     A               ; Double it
       MOV     E,A
       MVI     D,0
       LXI     D,TABLE
       DAD     D               ; HL points to the revelent table entry.
       MOV     E,M             ; Get the address
       INX     H               ; contained there,
       MOV     D,M             ; and move it
       XCHG                    ; into HL.
       PCHL                    ; Go do it.
;
FIN:
       ; Return to here when done.
       LHLD    SAVSTK
       SPHL                    ; Restore old stack pointer.
       LHLD    XITPAR          ; Get the result of the routine.
       MOV     A,L
       MOV     B,H             ; As per CP/M protocol.
       RET                     ; All done.
;
TABLE:
       ; Jump table for the various new BDOS calls.
       ; There should be one entry for each of "EXTRA" items.
NEW0:   DW      DONEW0
NEW1:   DW      DONEW1
       ...
       ...
;
NITPAR: DS      2               ; Save the entry value of DE.
XITPAR: DS      2               ; Save the result of the BDOS call.
       DS      40              ; Ample local stack -
                               ; change this if necessary.
SAVSTK: DS      2               ; Save the stack pointer on entry.
;
; Each of the following routines must end with a RET instruction.
; If the routine returns a value, then it should be stored in XITPAR.
;
DONEW0:                         ; Actual routine #0.

DONEW1:                         ; Actual routine #1.

       etc.