BIOS Video Service INT 10H (functions 9 and 13H) functionality
Some corrections


The LEAVE comments mention ES:SI where you intended DS:SI
If the comment for mov dx, 0x11e talks about "Line 2", then it should also say "Column 31" (in stead of "32")





add     dx, 255         Row 3, Column 30



This is a very nice approach! Although for reviewing purposes I had to read it a few times over, it is definitely better here than the simpler mov dx, 0x021d would have been.



Optimizations


When registers need to be preserved, it will be better to group the pushes and pops together. You've put them amongst the rest of the instructions and maybe because of this introduced the error of inverse restoration. A trick I've been using for many years now is numbering my push and pop instructions (see below). This facilitates enormously discovering these errors.

push    es            ;(1)
push    di            ;(2)

The calculation of the ES segment register can be shorter and clearer.

mov     ax, VIDEO_SEG ;0xB800
add     ah, bh        ;BH=Display page
mov     es, ax

The calculation for the video address in DI can be optimized a lot. Next code does it in 14 bytes, where your code used 18 bytes. Also it uses only half the instructions.

movzx   ax, dh        ;DH=Row
imul    ax, 80        ;80 characters per row
add     al, dl        ;DL=Column
adc     ah, 0         ;(*)
imul    di, ax, 2     ;Double for character+attribute AND bring in DI


(*) If you know that the counter in CX will always be reasonably small, then maybe write adc ah,ch here and shave off another byte.
In order to decide what the function should do you test the CX register using or cx, cx jz 553H. This can easily be shortened to jcxz 553H. A 2 byte gain.
Incrementing the resulting counter in CX will be shorter using inc cx because that's a 1-byte instruction.
The loop currently uses 2 jump instructions per iteration. This is wasteful in terms of speed and can easily be avoided as you'll see in below summary.


In summary

  push    es            ;(1)
  push    di            ;(2)
  mov     ax, VIDEO_SEG ;0xB800
  add     ah, bh        ;BH=Display page
  mov     es, ax
  movzx   ax, dh        ;DH=Row
  imul    ax, 80        ;80 characters per row
  add     al, dl        ;DL=Column
  adc     ah, 0         ;(*) Possibly use "adc ah, ch" with CH=0
  imul    di, ax, 2     ;Double for character+attribute AND bring in DI
  lodsb
  jcxz    Test          ;Till end of string marker
Char:
  push    cx            ;(3)
  rep     stosw
  pop     cx            ;(3)
  jmp     Exit
String:
  inc     cx
  stosw
  lodsb
Test:
  test    al, al
  jnz     String
Exit:
  pop     di            ;(2)
  pop     es            ;(1)
  ret