CP/M questions and a potential bug in RomWBW's BDOS?

239 views
Skip to first unread message

AngelaTheSephira

unread,
Jul 23, 2025, 10:54:14 PMJul 23
to retro-comp
Preface: I have an SC130 and I got it to learn assembly programming. I think I have learned something alright: it sucks! /lh
As the title suggests, I think I've found a bug in RomWBW's BDOS - function 10 (https://www.seasip.info/Cpm/bdos.html) does not seem to actually store the number of valid bytes in the buffer anywhere - both register A and buf_loc+1 do not contain anything but nulls which does not match the DR specifications for the BDOS. (A friend found somewhere that it's supposed to put the valid bytes length in A too - this is not mentioned on that site, however...) I've worked around it by prefilling the buffer area with the $ separator, but it is curious. Is this a RomWBW thing, or a 50 year old bug that was never fixed? If you need the code, I can share it.

Another thing is I'd like to open a file for reading / writing, but it's very unclear how one does so - function 15 seems to expect an FCB address, but how is that created? On top of this, if I want a new file to be created, how does CP/M know where I want it put? Part of me knows I could look at the BDOS myself, but I'm already suffering enough as it is and don't think I'd ever grasp the internals.

Any help will be appreciated!

Douglas Miller

unread,
Jul 23, 2025, 11:09:02 PMJul 23
to retro-comp
You want to consult the CP/M documentation, specifically "Section 5 - CP/M 2 System Interface" (might be titled differently depending on the manuals you find). This should tell you what you need to know to use the file functions.

And, yes, function 10 is supposed to return after setting the number of valid characters in buf_loc+1. Make certain you have set buf_loc+0 to the maximum (size of buffer) before calling. The documentation does not specify anything being returned in A, B, or HL for function 10, so if anything meaningful appears to be there you can't really depend on it in all versions.

AngelaTheSephira

unread,
Jul 23, 2025, 11:15:57 PMJul 23
to retro-comp
So it is a bug, good to know! Yes, I'm setting buf_loc+0 to 0FFh, but using a smaller size doesn't make the bug not happen so I do not think it's a problem with using the 8-bit maximum...

Do you have a link to the CP/M manuals? For what it's worth, I am running ZPM3 for development as I find it easier to work with since all the steps of development are happening on the machine itself.

Douglas Miller

unread,
Jul 24, 2025, 12:36:35 AMJul 24
to retro-comp
Here's one place that has the full manual, where chapter 5 is the System Interface guide: https://bitsavers.org/pdf/digitalResearch/cpm/CPM_Operating_System_Manual_Jul82.pdf

Wayne Warthen

unread,
Jul 24, 2025, 12:23:00 PMJul 24
to retro-comp
Hi,

The BDOS component of CP/M in RomWBW is unchanged from the DRI distribution.

Here is a small program that demonstrates using function 10.  This works for me.

Thanks, Wayne

ORG 100H

BDOS EQU 5

CR EQU 13
LF EQU 10

; READ A STRING FROM CONSOLE INTO BUF
MVI C,10 ; BDOS READ CONSOLE
LXI D,BUF ; BUFFER
CALL BDOS ; CALL BDOS

; SEND CR,LF,LF,DQUOTE TO CONSOLE
MVI A,CR ; CARRIAGE RETURN
CALL COUT ; SEND IT
MVI A,LF ; LINE FEED
CALL COUT ; SEND IT
MVI A,LF ; LINE FEED
CALL COUT ; SEND IT
MVI A,'"' ; DQUOTE
CALL COUT ; SEND IT

; SEND STRING ENTERED TO CONSOLE
LDA BUF+1 ; GET LENGTH RETURNED
MOV B,A ; PUT IN B FOR LOOP COUNTER
LXI D,BUF+2 ; DE POINTS TO STRING
LOOP: LDAX D ; GET NEXT CHAR
INX D ; BUMP TO NEXT
CALL COUT ; SEND CHAR TO CONSOLE
DCR B ; DECREMENT LOOP COUNTER
MOV A,B ; PUT IN ACUM
ORA A ; SET FLAGS
JNZ LOOP ; LOOP TILL DONE

; SEND DQUOTE,CR,LF TO CONSOLE
MVI A,'"' ; DQUOTE
CALL COUT ; SEND IT
MVI A,CR ; CARRIAGE RETURN
CALL COUT ; SEND IT
MVI A,LF ; LINE FEED
CALL COUT ; SEND IT

; RETURN TO CP/M
JMP 0 ; BACK TO CP/M

; WRITE CHARACTER IN A TO CONSOLE
; PRESERVING REGISTERS
COUT: PUSH B
PUSH D
PUSH H
MOV E,A
MVI C,2
CALL BDOS
POP H
POP D
POP B
RET

; CONSOLE INPUT BUFFER
BUF: DB 30 ; 30 CHARS MAX
DB 0 ; LENGTH RETURNED
DS 30 ; STRING RETURNED

END

AngelaTheSephira

unread,
Jul 24, 2025, 3:06:28 PMJul 24
to retro-comp
Interesting, I'll convert this to Zilog assembly and see if it works. I was trying to insert a $ character at the end +1, but perhaps something in that code was wrong? I don't think so though, it always places the delimiter at buf_loc+2 as if the length was zero...

AngelaTheSephira

unread,
Jul 24, 2025, 6:33:02 PMJul 24
to retro-comp
It did not work, seems the assembler is doing something funny potentially? Running the code at the end of the post results in this, and I believe I converted it to Zilog ASM correctly?
17:26 A1>zsm4 WW_TESTF.rel,WW_TESTF.lst=WW_TESTF.mac
Z80/Z180/Z280 Macro-Assembler V4.7

Errors: 0
Finished.

17:26 A1>link WW_TESTF
LINK 1.31

ABSOLUTE     0000
CODE SIZE    0120 (0100-021F)
DATA SIZE    0000
COMMON SIZE  0000
USE FACTOR     00

17:27 A1>ww_testf


+++ INVALID Z180 OPCODE @67C3H: FD CE 02 00 40 00 3F 54

IDE: IO=0x10 MODE=RC
IDE0: NO MEDIA
IDE1: NO MEDIA

ZSM4's input file follows...
.z180
ORG 100H
BDOS  equ 5
CR  equ 13
LF  equ 10
C_READSTR equ 10

; CONSOLE INPUT BUFFER
D_BUFLOC:    db  30      ; 30 CHARS MAX
db  0       ; LENGTH RETURNED
ds  30      ; STRING RETURNED
end


; READ A STRING FROM CONSOLE INTO BUF
ld c,C_READSTR
ld de,D_BUFLOC
call BDOS

; SEND CR,LF,LF,DQUOTE TO CONSOLE
ld a,CR
call COUT
ld a,LF
call COUT
call COUT
ld a,'"'
call COUT

; SEND STRING ENTERED TO CONSOLE
ld b,D_BUFLOC+1
ld de,D_BUFLOC+2
LOOP: ld a,(de)
inc d
call COUT
dec b
jp nz,LOOP

; SEND DQUOTE,CR,LF TO CONSOLE
ld a,'"'
call COUT
ld a,CR
call COUT
ld a,LF
call COUT

; RETURN TO CP/M
jp 0


; WRITE CHARACTER IN A TO CONSOLE
; PRESERVING REGISTERS
COUT: push bc
push de
push hl
ld e,a
ld c,2
call BDOS
pop hl
pop de
pop bc
ret

I mean, it's possible my code is horribly wrong, but I did compare the Intel ASM bytecode using https://svofski.github.io/pretty-8080-assembler/ to the equal instruction for the Z(1)80 and all of the instructions I used do exist in the opcode table...
Sorry if I've done something wrong!

AngelaTheSephira

unread,
Jul 24, 2025, 7:26:21 PMJul 24
to retro-comp
Alright, I figured it out. xd
It does seem to work, I wonder why the code from before wasn't working... Should I share the original version that didn't work?

AngelaTheSephira

unread,
Jul 24, 2025, 7:57:38 PMJul 24
to retro-comp
Alright, so I integrated the code that did work into my own testing program, and now it does something really strange - it freezes until you hit return, and then it continues as expected. It also lets you type in a single character and backspace it to replace it with a different one, but it doesn't seem to affect the actual buffer contents? Again, assembler is ZSM4 and the OS is ZPM3...

.z180
IF1
.PRINTX 'Pass 1'
ELSE
.PRINTX 'Pass 2'
ENDIF
TITLE "Testing program"
org 0100h
bdos equ 05h
C_WRITESTR equ 09h
C_READSTR equ 0Ah
C_WRITE equ 02h
ld bc,030h ;amount of space to clear
ld e,030h ;S H A D O W  C O U N T E R
ld a,00h ;nulls
L_CLR_BUF: ld hl,msginput ;Get memory buffer start location
add hl,bc ;Get memory offset into buffer
ld (hl),a ;Write the clearing value into buffer location
dec bc ;Decrease the counter
dec e ;Decrease the S H A D O W  C O U N T E R
jp nz,L_CLR_BUF ;loop
ld a,0FFh ;buffer size
ld (msginput),a ;Tell CP/M buffer size
ld c,C_WRITESTR
ld de,$startmsg
call bdos
ld c,C_READSTR
ld de,msginput
call bdos
call F_CRLF
ld c,C_WRITESTR
ld de,$finmsg
call bdos
ld hl,msginput+1 ;get the address into HL
ld b,(hl) ;transfer the contents from RAM to register
ld de,msginput+2 ;point the function at the start of string
call F_PRNSTR ;print the string
ld c,00h ;C_ABRT
call bdos ;return to CP/M

F_CRLF: ld c,C_WRITE ;function to call
ld e,0Ah ;Carriage return
call bdos ;print char
ld e,0Dh ;Line feed
call bdos ;print char
ret ;return

;The following two functions are courtesy of Wayne Warthen, thanks!
F_PRNSTR: ld a,(de) ;load the contents of RAM pointed to into A
inc de ;increment memory pointer
call F_COUT ;print the character
dec b ;decrement the length counter
jp nz,F_PRNSTR ;loop if not complete
ret ;return
F_COUT: push bc ;preserve registers
push de
push hl
ld e,a ;transfer character to print to E
ld c,C_WRITE ;function to call
call bdos ;print character to the screen
pop hl ;restore the registers
pop de
pop bc
ret ;return

$finmsg: db 'The text you typed was: $'
$startmsg: db 'Please enter text, up to 255 characters: $'
msginput ds 030h
end

I feel like there's something obvious I'm overlooking, but I don't know what it is...

Douglas Miller

unread,
Jul 24, 2025, 8:07:00 PMJul 24
to retro-comp
One common mistake made with Zilog assembly mnemonics is getting the operand correct so that the proper opcode is chosen ("ld" can mean any of a large number of machine opcodes, unlike Intel mnemonics where the assembler opcode is very specific). In your original code, you had "ld b,D_BUFLOC+1" when you really wanted "ld  b,(D_BUFLOC+1)" but that is not a valid instruction since the 8080 family is not truly symmetrical even though Zilog mnemonics fool you into thinking that. I used "zmac" to assemble your code and it threw an error at that line. It's possible a less-strict assembler might have just made that a "MVI B,D_BUFLOC+1" and not told you. Inspecting the listing file would have revealed it, but you'd have to look closely and know what the machine opcodes were supposed to be.

Wayne Hortensius

unread,
Jul 24, 2025, 8:17:56 PMJul 24
to retro...@googlegroups.com
On Thu, 24 Jul 2025 16:57:37 -0700 (PDT)
AngelaTheSephira wrote:

> Alright, so I integrated the code that did work into my own testing
> program, and now it does something really strange - it freezes until
> you hit return, and then it continues as expected. It also lets you
> type in a single character and backspace it to replace it with a
> different one, but it doesn't seem to affect the actual buffer
> contents? Again, assembler is ZSM4 and the OS is ZPM3...

Stripping out everything up to and including 'org 0100h', and
assembling with zmac and linking with zml, here's the result:

A2:SOURCE>>zmac test.z80


ZMAC Relocating Macro Assembler v 1.7, 04/09/93
Copyright 1988,1989 by A.E. Hawley

A2:SOURCE>>zml test.rel
ZML Linker V2.0- 04/01/93



Output file: A2:TEST.COM

LOAD_ADDR: RELOC_ADDR:
SEGMENT SIZE START STOP
CSEG 00D7 0100 01D6

Memory unused by linker..
FREE B63B 2DC4 E3FE

A2:SOURCE>>dump test.com

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF
0100 01 30 00 1E 30 3E 00 21 A7 01 09 77 0B 1D C2 07 .0..0>.!...w....
0110 01 3E FF 32 A7 01 0E 09 11 7D 01 CD 05 00 0E 0A .>.2.....}......
0120 11 A7 01 CD 05 00 CD 40 01 0E 09 11 64 01 CD 05 .......@....d...
0130 00 21 A8 01 46 11 A9 01 CD 4D 01 0E 00 CD 05 00 .!..F....M......
0140 0E 02 1E 0A CD 05 00 1E 0D CD 05 00 C9 1A 13 CD ................
0150 57 01 05 C2 4D 01 C9 C5 D5 E5 5F 0E 02 CD 05 00 W...M....._.....
0160 E1 D1 C1 C9 54 68 65 20 74 65 78 74 20 79 6F 75 ....The text you
0170 20 74 79 70 65 64 20 77 61 73 3A 20 24 50 6C 65 typed was: $Ple

00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 0123456789ABCDEF
0180 61 73 65 20 65 6E 74 65 72 20 74 65 78 74 2C 20 ase enter text,
0190 75 70 20 74 6F 20 32 35 35 20 63 68 61 72 61 63 up to 255 charac
01A0 74 65 72 73 3A 20 24 00 00 00 00 00 00 00 00 00 ters: $.........
01B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

A2:SOURCE>>test
Please enter text, up to 255 characters: Hello, world!
The text you typed was: Hello, world!
A2:SOURCE>>

AngelaTheSephira

unread,
Jul 24, 2025, 10:25:51 PMJul 24
to retro-comp
Interesting, I wonder if I have something wrong with my hardware...

Wayne Hortensius

unread,
Jul 24, 2025, 10:33:52 PMJul 24
to retro...@googlegroups.com
On Thu, 24 Jul 2025 19:25:51 -0700 (PDT)
AngelaTheSephira wrote:

> Interesting, I wonder if I have something wrong with my hardware...

First thing that comes to mind is to do a dump of your .com file and
see if it matches.

Wayne

Douglas Miller

unread,
Jul 24, 2025, 10:59:08 PMJul 24
to retro-comp
One problem is in your F_CRLF function. When you put 0Dh in E and call bdos, you don't set C with the BDOS function number (C_WRITE). The previous call to BDOS would have left the registers in an unknown state, so there's no telling what the BDOS will be trying to do.

AngelaTheSephira

unread,
Jul 24, 2025, 11:16:01 PMJul 24
to retro-comp
Ah, it was this! I'd have figured that the function would have restored the C register before returning so you could do rapid calls like this.

Thank you guys so much!

Phil G

unread,
Jul 25, 2025, 4:29:47 PMJul 25
to retro-comp
Sorry Angela but I'd like to step in and congratulate the responders here on their restraint, when someone who is 'learning assembly programming' states on a public forum that an excellent product from a highly respected source 'sucks', & that what is possibly the most complex Z80 code ever written has a fundamental bug, which of course turned out to be user error.  As a starting point when something goes wrong, a little humility is preferable to blazing guns   :-)
Cheers
Phil

Douglas Miller

unread,
Jul 25, 2025, 5:37:56 PMJul 25
to retro-comp
There have been times where various implementations got a corrupted BDOS (or other) image, but I'd expect Wayne's to be clean. Worth getting to the bottom of, though.

AngelaTheSephira

unread,
Jul 26, 2025, 2:19:48 AMJul 26
to retro-comp
Ah, I didn't say / intend to say RomWBW was bad - I was saying the assembly itself is bad. There's a lot of obviously useful instructions missing, like single register push/pop. It's mildly frustrating because apparently other CPUs do have these instructions, but the 8080 didn't have this ability and Zilog didn't add it even though they improved a lot of the other shortcomings of the 8080.

Regarding the bug: I only raised it as a bug because at the time I tested it, it was indeed returning nulls - testing again the next day showed it was no longer doing this. I was unaware if the BDOS was reimplemented from the specifications or if it was the original BDOS from DRI. Prior to this however, there have been strange intermittent faults (like the UART just... not working reliably) so I wanted to be sure it was not in fact a bug (hardware or software outside of what I was creating) but something I was doing wrong. ^^;

Sorry if it was insulting, that was not the intent.

Ronny Ribeiro

unread,
Jul 26, 2025, 7:45:44 AMJul 26
to AngelaTheSephira, retro-comp
I understood just like you said, Angela: assembly sucks XD
I feel that I'm gonna be a forever beginner XD
And yes, the guys were true gentleman here and the discussion was very productive and informative.
Thank you all!

--
You received this message because you are subscribed to the Google Groups "retro-comp" group.
To unsubscribe from this group and stop receiving emails from it, send an email to retro-comp+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/retro-comp/ad38136d-bc22-40b0-8f1b-5b90cf5296d8n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages