MAC SELECT construct

114 views
Skip to first unread message

Mr. Emmanuel Roche, France

unread,
Nov 6, 2021, 5:14:32 PM11/6/21
to
MAC SELECT construct by Mr. Emmanuel ROCHE, France
--------------------

Ok. As usual, it started innocently. While searching for something else, I saw a mention of Henry Ledgard, the author of the classic "Programming Proverbs". This man wrote only a handful of books, but I find them much more interesting than others (and well written). There is also the famous "The emperor with no clothes" article, published in the "Commun. ACM" in 2001. "Nothing new under the Sun", alas!

I made a search. Curiously, Wikipedia has no page on him. I found a Web page listing some of his books... in Germany!

https://dblp.org/pid/26/6957.html

It is there that I found mention of "A Genealogy of Control Structures", an article that I had not read. 36 pages later, my interest in "Control Structures" was revived. Now, it happens that, 6 weeks ago, there was a thread, in the comp.os.cpm Newsgroup, about SEQIO.LIB, a library of macros that was published in the "CP/M MAC Macro Assembler" manual, starting in 1977. And I remembered that this manual happens to contain on "Program Control Structures" Section...

As usual, I have no idea where my stuff is, so I searched on the Internet if someone had uploaded the MAC manual. I found the 1980 version, that I downloaded, then printed the pages of the Section.

As Bohn & Jacopini demonstrated in 1966(!), everything that can be programmed can be reduced to only 3 "control structures":

1) sequence of instructions (LET... LET...)
2) controled iteration (FOR-NEXT)
3) case selection (IF-THEN-ELSE)

https://en.wikipedia.org/wiki/Structured_program_theorem

Depending on their domains, some Programming Languages have more specialized control structures but, to be "universal", a PL must have, one way or another, at least those 3 control constructs.

So, the MAC manual contains:

1) WHEN-ENDW (conditional grouping)
2) controlled iteration (DO-ENDDO)
3) case selection (SELECT-ENDSEL)

(a sequence of ASM instructions should be obvious)

allowing one to produce Intel 8080 assembly languages with those high-level control constructs. Very educational. Nothing like that can be found in MicroShit manuals. You can feel that Gary Kildall was a Teacher of Computer Science.

Ok. Now, how to recreate those macros? At my age, with my sight, it would be a pain in the Accumulator to retype the source codes. I searched inside my computer, and found one directory named "MAC Macros". One less work to do. I found them unreadable since they are written in upper case only. (When Gary Kildall created Digital Research, he complained that he had heard the ASR-33 Teletype during 2 years at the Naval Postgraduate School of Monterey, California! Since CP/M first booted in April 1974 and he created DRI in 1976, he was right.) So, I edited the files to be more readable. Now, time for a test.

I assembled the first example program. No problem, except that the messages displayed on the screen were in upper case, when the source file was in lower case. But this gives an "Old Style" to the programs.

I continued. No problem, until I arrived to SELECT, the last construct.

After 30+ years of assembly programming, I could not escape the fact the hex columns of the PRN files were 3 bytes away from the published PRN files! All the other ASM files had produced the same PRN files as the MAC manual, but not this one!?!

I wondered: "Maybe they changed something in this 1980 revision?" So, I searched for the 1977 version, and found it on BitSavers. However, impossible to find a difference!

So, I made another search among the files of my Internet computer, and found a ZIP file containing the MAC macros. When I used it (I mean: SELECT.LIB), it produced the PRN files of the manual! Haaaaa!... (the programmer's national anthem). All I had to do, now, was to find the difference. Which is: one line was missing in this version, which has been present since 1977 in the MAC manual. So, apparently, this file was a copy of the MAC macros made circa 1976, when Digital Research was launched, and religiously preserved ever since then.

I think that I am a little bit knowledgeable about CP/M and its utilities, but I have never heard of this bug. So, in case someone else understands what I write, here are the "bugs" that I found in the MAC manual:

MAC Section 9.3 errata
----------------------

Page 117, second paragraph from the bottom of the page:

"Upon encountering [THE] an ENDW statement in the source program, ..."

Page 123, third paragraph from the bottom of the page:

(...) the CR-LF on each i[N]teration, ..."

Page 128, bottom of SELECT macro:

select var
selnext ;; Automatically select case 0
ENDM

Page 134, bottom of Figure 51:

WRITE <so I^'^'m going back^!>

Of course, if you use SELECT with the SELNEXT line, the 3 listings of Figure 50 will no longer correspond with the ones of the MAC manual. Apparently, I am the first person to notice this in the last 44 years. This must be progress? (Re-read the "Emperor" article.)

Yours Sincerely,
Mr. Emmanuel Roche, France

Martin

unread,
Nov 7, 2021, 8:42:12 AM11/7/21
to
Am 11/06/2021 10:14 PM, Mr. Emmanuel Roche, France schrieb:
> MAC SELECT construct by Mr. Emmanuel ROCHE, France
> --------------------

[...]

> So, I made another search among the files of my Internet computer,
and found a ZIP file containing the MAC macros.
When I used it (I mean: SELECT.LIB), it produced the PRN files of the manual!
Haaaaa!... (the programmer's national anthem).
All I had to do, now, was to find the difference.
Which is: one line was missing in this version,

The correct version of the macro *is* the newer one without the SELNEXT line.

> I think that I am a little bit knowledgeable about CP/M and its utilities,
but I have never heard of this bug.
So, in case someone else understands what I write,
here are the "bugs" that I found in the MAC manual:
>
> MAC Section 9.3 errata
> ----------------------
>
> Page 117, second paragraph from the bottom of the page:
> "Upon encountering [THE] an ENDW statement in the source program, ..."
>
> Page 123, third paragraph from the bottom of the page:
> (...) the CR-LF on each i[N]teration, ..."
>
> Page 128, bottom of SELECT macro:
>
> select var
> selnext ;; Automatically select case 0

This line above was *removed* in the fixed version.

> ENDM

>
> Page 134, bottom of Figure 51:
>
> WRITE <so I^'^'m going back^!>
>
> Of course, if you use SELECT with the SELNEXT line,
the 3 listings of Figure 50 will no longer correspond with the ones of the MAC manual.
Apparently, I am the first person to notice this in the last 44 years.
This must be progress? (Re-read the "Emperor" article.)
>

And if we are at it, here are real *BUGS* !!!

First a test progam "compbug.asm" to show the problem.
It compares the correct "LEQ" with the buggy "GTR" Macro.

===== 8< =====
maclib compbug

bdos equ 0005h
arg1 equ 005dh
arg2 equ 006dh

org 100h

mvi e,'1'
lda arg1
leq ,arg2,xleqa
dcr e
xleqa:

mvi d,'0'
lda arg1
gtr ,arg2,xgtra
inr d
xgtra:

xchg
shld msg1

mvi c,9
lxi d,msg1
call 5

ret

msg1: ds 2
db 0dh,0ah,'$'
===== 8< =====


Compile...

B>mac compbug.aaa
CP/M MACRO ASSEM 2.0
0133
002H USE FACTOR
END OF ASSEMBLY

B>load compbug

FIRST ADDRESS 0100
LAST ADDRESS 0132
BYTES READ 0031
RECORDS WRITTEN 01


Execute with different values...

B>compbug 2 1
00

B>compbug 2 2
10

B>compbug 2 3
11


Doesn't look very right ???



Both "COMPARE.LUB" (Page 107, Figure 41) and
"NCOMPARE.LIB" (Page 112, Figure 43a) are at fault:

Replace the lines
DCR A
JNC TL
with
JNZ TL


1) Macro "GTR" in "COMPARE.LUB"

Fixed version:
===== 8< =====
;
GTR MACRO X,Y,TL
;; X GREATER THAN Y TEST
LOCAL FL ;;FALSE LABEL
TEST? X,Y
JC FL
JNZ TL
FL: ENDM
===== 8< =====


2) Macro "GTR" in "NCOMPARE.LUB"

Fixed version:
===== 8< =====
;
GTR MACRO X,Y,TL,FL
;; X GREATER THAN Y TEST
IF NUL TL
LEQ X,Y,FL
ELSE
LOCAL GFL ;;FALSE LABEL
TEST? X,Y
JC GFL
JNZ TL
GFL: ENDM
===== 8< =====


3) Macro "LEQ" in "NCOMPARE.LIB" has another BUG:

The line
GEQ X,Y,FL
should be replaced with
GTR X,Y,FL

The fixed version:
===== 8< =====
;
LEQ MACRO X,Y,TL,FL
;; X LESS THAN OR EQUAL TO Y TEST
IF NUL TL
GTR X,Y,FL
ELSE
LSS X,Y,TL
JZ TL
ENDM
===== 8< =====


> Yours Sincerely,
> Mr. Emmanuel Roche, France
>

Have fun!
Martin

Martin

unread,
Nov 7, 2021, 8:57:44 AM11/7/21
to
Ooops!

While testing, I renamed the buggy "COMPARE.LIB" to "COMPBUG.LIB".
You likely didn't, so please change "maclib compbug" back to "maclib compare".


Martin

Mr. Emmanuel Roche, France

unread,
Nov 7, 2021, 10:09:26 AM11/7/21
to
"Martin" <this.is...@so.its.invalid> wrote:

> The correct version of the macro *is* the newer one without the SELNEXT line.

? The MAC manual was published in 1977, then revised in 1980. In both versions, we find the following remark:

"At the end of the redefined SELECT macro, SELNEXT is invoked automatically to delimit the first case in the SELECT group (otherwise SELECT would have to be followed immediately by SELNEXT in the user program to generate the proper labels)."

So, it seems quite surprising that nobody at DRI would have noticed that the SELNEXT line was... er, redundant?

Also, why would later versions of the WHEN library contain this line, while only one copy found so far (presumed to be the oldest one) contains it?

At least, the MAC manual documents this line.

(Regarding your bugs, tomorrow, I am busy. So, I will investigate them later. Also, it would be more logical to use the SIMPIO library. Macros exist to make more high level assembly language programs, so why not use them?)

Martin

unread,
Nov 8, 2021, 2:10:47 PM11/8/21
to
Ahhhh! :-)
Finally, I see it!

The SELNEXT was not added or removed,
it was moved into the redefined SELECT Macro!

The buggy version in the manual from 1977 only works
*THE FIRST TIME*!

The SELNEXT is only invoked in the original SELECT,
the redefined SELECT does not contain the SELNEXT anymore.

[...]
XCHG ;;READY BRANCH ADDRESS IN HL
PCHL ;;GONE TO THE PROPER CASE
ECNT SET 0 ;;ELEMENT COUNTER RESET
ENDM
;; INVOKE REDEFINED SELECT THE FIRST TIME
SELECT VAR
SELNEXT ;;AUTOMATICALLY SELECT CASE 0
ENDM
[...]


In the correct version of SELECT.LIB inside
<http://www.retroarchive.org/cpm/archive/unofficial/download/mac-b.zip>
SELNEXT is invoked *EVERY TIME* the redefined SELECT Macro is used.

This is the correct version, comparing the output of the sample program
at page 130 with both versions of SELECT.LIB shows it clearly.

[...]
XCHG ;;READY BRANCH ADDRESS IN HL
PCHL ;;GONE TO THE PROPER CASE
ECNT SET 0 ;;ELEMENT COUNTER RESET
SELNEXT ;;SELECT CASE 0
ENDM
;; INVOKE REDEFINED SELECT THE FIRST TIME
SELECT VAR
ENDM
[...]


Martin

Mr. Emmanuel Roche, France

unread,
Nov 8, 2021, 4:13:39 PM11/8/21
to
I wrote: "As usual, it started innocently." Hahaha! Indeed! I have been wondering all day long how to prove the good functioning of macros.

Along the way, I found:

1) The ASM file of Figure 44A and the PRN file of Figure 44B should have the following lines indented one way or another, to show the generated code:

GTR x,%'z',NoTran
lda x
ani 0101$1111b ; Clear lower case bit
sta x ; Store back to X

2) The pseudo-code between Figure 49b and Figure 50a should have the following 2 lines:

LDA id
LXI H,SelV0

3) I have had a little bit of difficulty understanding what "Martin" was trying to say. In short, the macro SELECT must contain one SELNEXT line. The problem is that, since 1977, the MAC manual was showing it in the wrong place in the SELECT macro! So, the good code is:

ecnt SET 0 ;; Element counter reset
selnext ;; Select CASE 0
ENDM
;; Invoke redefined SELECT the first time
select var
ENDM

(In English: the SELNEXT in *INSIDE* the firs ENDM, not before the second ENDM.)

As usual, being a programmer, I prefer that the code do the talking.

I think that the following is reasonable proof that this SELECT construct works:

A>fig51

SAMPLE CONTROL STRUCTURES
TYPE SINGLE CHARACTERS FROM
A TO D, I'LL STOP ON D
TYPE A CHARACTER: e
BAD CHARACTER
TYPE A CHARACTER: a
YOU SELECTED CASE A
TYPE A CHARACTER: b
YOU SELECTED CASE B
TYPE A CHARACTER: c
YOU SELECTED CASE C
TYPE A CHARACTER: d
YOU SELECTED CASE D
SO I'M GOING BACK!
A>

4) As you may have seen, macros are funny. So, I have more questions.

- Anybody knows how to change WRITE so that it does not convert all the strings to upper case?

- Anybody has a working FOR-NEXT construct?

- Anybody has an IF-THEN-ELSE construct? (Since these are "keywords" of MAC, one way would be to use the French words SI-ALORS-SINON. The closing ENDIF would then be ENDSI.)

dxforth

unread,
Nov 8, 2021, 11:07:34 PM11/8/21
to
On 9/11/2021 08:13, Mr. Emmanuel Roche, France wrote:
> ...
> 4) As you may have seen, macros are funny. So, I have more questions.
>
> - Anybody knows how to change WRITE so that it does not convert all the strings to upper case?
>
> - Anybody has a working FOR-NEXT construct?
>
> - Anybody has an IF-THEN-ELSE construct? (Since these are "keywords" of MAC, one way would be to use the French words SI-ALORS-SINON. The closing ENDIF would then be ENDSI.)

Perhaps it's just me but simulating HLL control structures in asm
adds a level of abstraction I can do without. This after seeing
Forth do exactly that and me struggling to remember what conditional
jump it was actually compiling. OTOH SEQIO.LIB is worth learning
because it's a mini-application and not just a clever alternate way
of doing something. YMMV

Mr. Emmanuel Roche, France

unread,
Nov 10, 2021, 5:15:19 PM11/10/21
to
"As usual, it started innocently." This must be the understatement of the week.

An "Old Timer" of the comp.os.cpm Newsgroup, who has not published anything for years, sent me his collection of macros. Unfortunately, they mostly come from the MAC manual.

However, when browsing them, I had a shock. The statements of the IF-ELSE-ENDM constructs were *INDENTED*! I could not believe my eyes! When I program in high-level languages, I do this automatically, and when I retyped the libraries, I just rewrote them in lower case, and did not, for some reason, indent them!

Since the NCOMPARE library contains at least 6 macros with IF-ELSE-ENDM constructs, indenting them change dramatically the appearance of the library! Ole, you are a genius!

It is incredible what a well-placed TAB can do.

Finally, you may have remarked that the comments, at the beginning of Figure 51, says that the program will stop on the "Z" character, while it uses "D"...

Mr. Emmanuel Roche, France

unread,
Nov 11, 2021, 12:59:49 PM11/11/21
to
I think that I have answered the question:

> Anybody knows how to change WRITE so that it does not convert all the strings to upper case?

if I judge by what the programs say:

A>fig42

Type a character from A to D: e
Not an A, B, C, or D
Type a character from A to D: a
You typed an A
Type a character from A to D: b
You typed a B
Type a character from A to D: c
You typed a C
Type a character from A to D: d
You typed a D
Bye
A>fig44

Type a character from A to D: e
Not an A, B, C, or D
Type a character from A to D: a
You typed an A
Type a character from A to D: b
You typed a B
Type a character from A to D: c
You typed a C
Type a character from A to D: d
You typed a D
Bye
A>fig46

Type a character from A to D: e
Not an A, B, C, or D
Type a character from A to D: a
You typed an A
Type a character from A to D: b
You typed a B
Type a character from A to D: c
You typed a C
Type a character from A to D: d
You typed a D
Bye
A>fig48

Type the stop character: e
Type a character: a
You typed an A
Type a character: b
Not an A
Type a character: c
Not an A
Type a character: d
Not an A
Type a character: e
Not an A
Stop character
Bye
(...)
A>fig51

Sample control structures
Type single characters from
A to D, I will stop on D.
Type a character: e
Bad character
Type a character: a
You selected case A
Type a character: b
You selected case B
Type a character: c
You selected case C
Type a character: d
You selected case D
So I am going back.
A>That's All, Folks!
Reply all
Reply to author
Forward
0 new messages