STRUCT79 - CP/M

This is a discussion on STRUCT79 - CP/M ; Dear M. Roche, Thank you very much for the text version of the Report. You have done an amazing amount of work in a short time to produce this. Please forgive my curiosity, but why does this 30-year-old report interest ...

+ Reply to Thread
Results 1 to 5 of 5

Thread: STRUCT79

  1. STRUCT79

    Dear M. Roche,

    Thank you very much for the text version of the Report. You have
    done an amazing amount of work in a short time to produce this.

    Please forgive my curiosity, but why does this 30-year-old report
    interest you? I had almost forgotten it.

    In 1975, before Bill Gates was raised to divine status, I needed a
    user-friendly text editor for a project I was involved with, and
    worked on the design of the ivi editor. The editor became used as
    a typing tool across this University and, for a short time, was
    even sold commercially. (It is still in use, and now incorporates
    my colleague's and my VINCI natural language generating system.)

    The host machine for the editor was the PDP-11/03, and at that
    time, the machine's assembly language, MACRO-11, was the only
    suitable language available on it. To simplify the task of writing
    a large program with such primitive features, my programmer and I
    devised and implemented the collection of macros described in the
    Report. At that time I was also teaching a course on machine-level
    programming to a class of about 150 undergraduates, and after the
    first few weeks, the macros permitted the students to write larger
    programs than would have been possible without them. A few other
    Universities requested copies, and also used them in their courses.

    By the early 1980s, however, I had moved on to UNIX-based machines
    with C compilers and was no longer teaching that course. So I no
    longer had any use for the macros, and (for me) they passed into
    history. I was very surprised when you asked for a copy of the
    Report.

    Anyway, thank you again for the text version.

    Best wishes.

    Michael Levison

    PS. Please use Michael rather than Sir. Queen Elizabeth has not
    yet granted me a Knighthood!




  2. Re: STRUCT79

    LEVISON.WS4
    -----------

    Technical Report No.79-88

    - "STRUCT79:
    A Macro Package to Simulate High-Level Control Structures in MACRO-11"
    Dr. Michael Levison
    Department of Computing and Information Science
    Queen's University, Kingston, Ontario, Canada

    (C) 1979 M. Levison

    (Retyped by Emmanuel ROCHE.)


    Introduction
    ------------

    STRUCT is a set of MACRO-11 macros which give this assembly language
    the
    appearance of possessing high-level control structures, including
    conditional,
    iterative and CASE statements, declarations, procedures and
    interrupt
    procedures. These have been used extensively both in large and small
    programs
    to simplify programming, and to improve structure and readability. The
    first
    version of STRUCT was described in Technical Report 77-53; the
    version
    described here represents both a considerable extension and a
    reimplementation
    of existing features. There are a few additional reserved words
    but,
    otherwise, existing programs should be unaffected, unless they make use
    of
    implementation details.

    This report is self-contained, and incorporates all relevant material from
    the
    earlier one.


    Simple conditions
    -----------------

    Conditions take one of the forms:




    where
    is any MACRO-11 operand
    and
    is .EQ. | .LT. | .HIS. | ...

    (i.e., any similar operator derivable from a
    PDP-11
    conditional branch.)

    The first of these is analogous to a compare and a branch, the second to
    a
    test and branch, the third to a branch only.


    Examples:

    %1 .GT. #4 [ R1 > 4 ] ?
    X(R2) .LT. Y [ X(R2) < Y ] ?
    (R6)+ .NE. [ (R6)+ ^= 0 ] ?
    .EQ. [ Z BIT set ] ?

    Conditions are used within WHILE, REPEAT, and conditional statements.
    The
    construction of more complex conditions, including those linked with
    Boolean
    operators, is discussed in a later section.


    WHILE statements
    ----------------

    WHILE statements take the form:

    WHILE

    ENDWHILE
    where
    is any MACRO-11 code.

    The statement allows to be excuted repeatedly while the condition
    is
    true. If the condition is omitted, an infinite loop is formed
    (and,
    presumably, there must be some other form of exit).

    WHILE may be replaced by any of WHILB, WHILJ, WHILBJ. WHILB and WHILBJ
    cause
    CMPB and TSTB instructions to be generated, instead of CMP and TST. WHILJ
    and
    WHILBJ cause JMP instructions to be generated between distant parts of
    the
    statement, instead of branches. WHILEB and WHILEJ are synonyms for WHILB
    and
    WHILJ, respectively.

    In all cases, the operands are evaluated only once per loop, so that
    any
    MACRO-11 operands, including auto-increments, may be used in the conditions.

    There is a DO macro which may be placed on the line preceding the body of
    the
    loop. It is normally omitted, but must be present if the loop contains
    an
    'inverting' complex condition (see below). The DO macro may have another
    valid
    MACRO-11 instruction (including one of these structure macros) on the
    same
    line, if desired.


    REPEAT statements
    -----------------

    REPEAT statements take the form:

    REPEAT

    ENDREPEAT

    and cause to be executed repeatedly. They normally contain either
    an
    UNTIL or a COND clause, giving rise to REPEAT-UNTIL or
    REPEAT-WHILE
    statements. If no such exit is present, an infinite loop is formed.

    No jump variant has been implemented, in this case. The terminating
    macro
    itself determines whether to assemble a branch or jump (but
    conditional
    clauses too far from the ENDREPEAT will cause errors).

    The REPEAT macro may have another valid MACRO-11 statement on the same
    line,
    if desired.


    Conditional statements
    ----------------------

    Conditional statements take the form:

    IF
    THEN
    ELSE
    ENDIF

    The ELSE part may be omitted, and IF may be replaced by any of IFB, IFJ,
    IFBJ.
    The interpretation is analogous to that of the WHILE statement.

    The THEN and ELSE macros may have another valid MACRO-11 statement
    following
    on the same line, if desired.


    FOR statements
    --------------

    FOR statements take the form:

    FOR FROM TO BY

    ENDFOR

    and cause the to be executed for each value of , starting
    with
    , and goung UP in increments of , as long as does not
    exceed
    . All or any of the parts FROM , TO , and BY may
    be
    omitted. In default, the iteration will begin at the current value of
    ,
    the loop end test will be omitted, and the increment will be taken to be
    1,
    respectively. Note that the increment must be positive. For
    negative
    increments, FOR is replaced by FORDN. FORJ and FORDNJ provide JMP versions
    of
    these macros. and are referenced only once per cycle, but
    is
    referenced one additional time during loop initialization. is
    referenced
    several times per cycle, so that auto-increment operands should not be
    used.
    is called only once for the entire statement.

    An alternative iterative statement takes the form:

    DECR FROM

    ENDDECR

    where is the name of a register. This causes to be obeyed
    while
    the register takes each value from down to 1. Its purpose is to allow
    the
    programmer access to the SOB instruction on those PDP-11s which have it.
    FROM
    may be omitted, in which case the iteration starts at the current
    value
    of the register. Note that must not be too large (that is to say:
    it
    must conform to the limit imposed by the SOB instruction), and that the
    loop
    test follows loop body (so that the register is left at 0).


    Complex conditions
    ------------------

    Complex relational expressions, combinations of several conditions,
    loop
    exits, REPEAT-UNTIL statements, and so on, can be put together using
    COND
    (synonymously AND), OR, and UNTIL macros, which take the form:

    COND

    These macros may be used in any of the (vertical) combinations:

    AND UNTIL OR UNTIL UNTIL
    AND UNTIL OR OR AND
    AND UNTIL OR OR AND
    ... ... ... ... ...
    AND UNTIL OR OR AND

    The first four of which may appear within WHILE, FOR, REPEAT, or
    IF
    statements; the last in REPEAT statements only.

    The sequence of AND macros causes a branch to the exit (or, in the case of
    an
    IF statement, to the ELSE part where one exists) when a false condition
    is
    found.

    The sequence of UNTIL macros causes a branch to the loop exit when a
    true
    condition is found. (Note that this combination does not read well in
    IF
    statements.) The UNTIL-OR sequence is synonymous.

    The sequence of OR macros used in WHILE, IF, or FOR statements causes a
    branch
    to the body of the loop (or, in the case of an IF statement, to the THEN
    part)
    if any of the conditions, including the one on the WHILE, IF, or FOR, is
    true.
    This sequence is one of two referred to as 'inverting' (it inverts the
    meaning
    of the preceding WHILE, and causes the interchange of certain
    subsequent
    labels). It imposes the following special requirements: the first OR
    macro
    must follow immediately after the WHILE, IF, or FOR; the body of the loop
    (or
    the THEN part) must be preceded by a DO (or THEN) macro; and the DO (or
    THEN)
    must immediately follow the final OR.

    The sequence of OR macros used in a REPEAT statement is not 'inverting',
    and
    is synonymous with the sequence of UNTILs.

    The UNTIL-AND sequence (used in a REPEAT statement only) causes a branch
    to
    the loop body if any of the conditions if false. This, too, is an
    'inverting'
    sequence. The first AND must follow immediately after the UNTIL, and
    the
    ENDREPEAT immediately after the final AND (no DO is necessary; the
    ENDREPEAT
    embodies one).

    Except in the cases mentioned, the individual ANDs, ORs, and UNTILs of
    a
    sequence may be separated by sections of code. Applications are given below.

    As the examples shows, the sequences specified produce 'obvious'
    effects.
    Other combinations, notably those which mix AND and OR, do not, and should
    be
    avoided. Nor should more than one of these sequences be included in the
    same
    structure.

    If a byte condition is to be tested, CONDB, ORB, and UNTILB are
    substituted
    for COND, Or, and UNTIL. AND and ANDB are synonyms for COND and CONDB.
    There
    are no 'jump' forms of the macros, the compilation of branches or jumps
    being
    determined by the statement in which a macro occurs.


    Examples:
    ---------

    (1) To perform a loop while (p+q)=(r+s)

    WHILE
    mov p,x
    add q,x ; x = p+q
    mov r,y
    add s,y ; y = r+s
    COND x .EQ. y

    ENDWHILE


    (2) To perform a piece of code if two conditions p>q and r>s hold:

    IF p .GT. q
    AND r .GT. s
    THEN
    ENDIF

    As many ANDs as desired may be included, each optionally preceded by some
    code
    which assists in computing the condition. The 'assisting' code may
    contain
    other structures nested within it (including other IF and WHILE
    statements
    which contain CONDs, etc.)


    (3) IF x .LT. #3 (4) WHILE y .EQ.
    OR x .GE. #20 OR z .EQ.
    THEN DO

    ENDIF ENDWHILE

    Again, as many ORs as desired may be included, but the first may have
    no
    'assisting' code. Note, too, that the THEN/DO abuts on the final OR.


    (5) REPEAT (6) REPEAT UNTIL x .HIS. y

    UNTIL p .LT. q ENDREPEAT
    ENDREPEAT


    (7) FOR i FROM #1 (8) REPEAT
    UNTIL r1 .GT. r2
    UNTILB char .EQ. #040
    ENDFOR ORB char .EQ. #'A
    ENDREPEAT


    Note:
    The user should remember that conditions containing operands cause
    the
    assembly of CMP or TST instructions, which alter the PDP-11 condition
    codes.
    Hence, in multiple conditions, those without operands should be placed
    first;
    e.g.

    IF .EQ.
    AND p .GT. p

    determines whether the Z-bit is set and whether p>q, but the reverse does
    not,
    because the comparison of p and q alters the Z-bit.


    CASE statements
    ---------------

    CASE statements take the form:

    CASE
    STMNT ,,...

    STMNT ,...

    ...
    ELSE
    ...
    ENDCASE

    and may be either one- or two-dimensional.

    If the CASE statement is one-dimensional, is a word operand
    (evaluated
    only once), and , , ..., are integers; if it is
    two-dimensional,
    is a pair of word operands (each evaluated only once), and
    ,
    , ..., are each pairs of integers. Each pair of integers and operands
    is
    enclosed in angle-brackets ("<" and ">").

    The effect of a CASE statement is to evaluate , locate its value among
    the
    integers (or pairs) which follow the STMNT macros, and then execute
    whichever
    one of , , ..., follows that STMNT.

    Each STMNT may be followed by several integers (or pairs), a STMNT without
    a
    parameter being ignored.

    The optional ELSE part provides a target for all cases *within 'jump
    table
    range'* which do not appear in STMNT macros. If the ELSE is omitted,
    these
    cases simply cause a jump past the CASE statement. All such cases are
    flagged
    with a warning message during assembly. Note that the execution of a
    CASE
    outside 'jump table range' is not detected, and its effect is
    potentially
    disastrous. ELSE may, as usual, have another MACRO-11 statement on the
    same
    line.

    'Jump table range' is defined as follows:


    One-dimensional
    ---------------

    If n is the largest integer which appears in a STMNT macro, the range is 0
    to
    n.


    Two-dimensional
    ---------------

    For every pair which occurs in a STMNT macro, the range includes
    ,
    , ..., (all earlier columns of the same row), and also
    <0,0>,
    <1,0>, ..., (the zero column of each earlier row).

    Thus, the appearance of <0,2>, <3,3>, and <5,2> in STMNTs causes:

    <0,0> <0,1> <0,2>
    <1,0>
    <2,0>
    <3,0> <3,1> <3,2> <3,3>
    <4,0>
    <5,0> <5,1> <5,2>

    to be in jump table range.

    For small integer byte operand(s), CASE is replaced by CASEB. In the
    two-
    dimensional case, both operands are either bytes or words (i.e., not
    mixed).
    If CASE, CASEB are replaced by CASEJ, CASEBJ, JMP instructions will
    be
    generated, instead of branches.


    Warnings
    --------

    (1) In estimating the size of code, it should be noted that a jump
    table
    is generated at the position of the ENDCASE. The table contains
    one
    element for every CASE number (or pair) in jump table range, and
    an
    additional element for every 'row' in the two-dimensional case.

    (2) Note that

    STMNT 1,2


    is NOT equivalent to

    STMNT 1
    STMNT 2


    The second causes the null statement to be executed for value 1 of
    the
    operand.

    (3) A jump to a label placed on a STMNT macro will cause control to
    pass
    to the *end* of the CASE statement, not to the code following
    the
    STMNT.

    (4) The ELSE part may be positioned anywhere in the CASE statement
    *after*
    the first STMNT.


    Procedures
    ----------

    procedure declarations take the form:

    PROCEDURE GLOBAL

    ENDPROC

    where is the procedure name, and the register and
    parameter
    GLOBAL are optional.

    Procedure calls take the form:

    ,,...

    where ,... are optional MACRO-11 word operands.

    The effect of the call depends on the register parameter of the
    declaration.
    If the register parameter is absent or is R7, the effect is to place
    the
    operands on the stack (in reverse order) and call the procedure. Thus,
    inside
    the procedure, will be found in 2(R6), in 4(R6), and so on,
    so
    long as the procedure itself does not alter the stack. Encountering
    ENDPROC
    causes the stack to be cleared, and control to be returned to the
    calling
    program.

    If the register parameter is some other register, then this register is
    used
    as the linkage register in the JSR instruction (and in the corresponding
    RTS),
    and the parameters are placed in line after the call. On entry to
    the
    procedure, the linkage register will be found to point at the first
    parameter,
    so that is in @Rx, in 2(Rx), and so on. (Note the
    discrepancy
    between this and the stack form of procedure, caused by the presence of
    the
    return address on the stack.)

    In this latter case, it is the responsibility of the procedure body to
    ensure
    that the linkage register is pointing at the word following the
    final
    parameter before the ENDPROC is encountered. Recall that the use of a
    register
    as linkage register does not affect its global contents, since the JSR and
    RTS
    instructions preserve and restore it.


    Examples:
    ---------

    (1) A procedure with R7 as register parameter.

    PROCEDURE FRED
    ; Suppose the procedure expects five parameters,
    ; they will be in 2(R6), 4(R6), 6(R6), ... resp.

    ENDPROC

    FRED R2, #45., PQR, 2(R3), @R4

    This call actually generates:

    MOV @R4,-(R6)
    MOV 2(R3),-(R6)
    ...
    JSR R7,FRED
    ADD #5+5,R6 ; Clean up stack

    (2) A procedure with R3 as register parameter.

    PROCEDURE FN R3
    ; Suppose the procedure expects five parameters,
    ; they will be in @R3, 2(R3), 4(R3), ... resp.

    ENDPROC

    FN XYZ, #A, #5, R2, 4(R4)

    This call actually generates:

    MOV XYZ,p1
    MOV R2,p4
    MOV 4(R4),p5
    JSR R3,FN

    p1: .WORD ; Space to receive contents of XYZ
    p2: .WORD A
    p3: .WORD 5
    p4: .WORD ; Space to receive contents of R2
    p5: .WORD ; Space to receive contents of 4(R4)

    where p1,p2,p3,p4,p5 denote local addresses computed by the calling macro.
    On
    entry, R3 contains p1; before exit, the procedure body must ensure that
    R3
    contains the address of the word following p5.

    If the parameter GLOBAL is present, the procedure will be available to
    be
    called from other modules. In each such other module, there must be
    an
    'external' declaration of the module:

    PROCEDURE EXTERNAL

    A procedure must be declared before it is called. To allow a call to
    occur
    earlier in the module than the procedure body, a forward declaration
    is
    provided, in the form:

    PROCEDURE FORWARD

    In these two cases, there is, of course, no code, and no ENDPROC.


    Warnings
    --------

    (1) With either of the two call types, only WORD operands are
    permitted,
    failing which, an addressing error may occur.

    (2) There must be exactly one ENDPROC per procedure; further returns
    can,
    if necessary, be effected by RTS instructions with the
    appropriate
    linkage register (R7 if absent).

    (3) The system will not detect the discrepancy if the user
    specifies
    different linkage registers in the body declaration and the
    EXTERNAL
    and/or FORWARD declarations of the same procedure.


    PUSH and POP
    ------------

    The macro calls

    PUSH ,,,...
    and
    POP ...,,,

    cause the specified MACRO-11 operands to be placed on, and removed from
    the
    stack, respectively. PUSHB and POPB variants are also available for
    the
    manipulation of byte variables. If PUSH occurs without a parameter, a
    cleared
    word is pushed onto the stack. If POP occurs without a parameter, stack
    is
    popped, and the top item lost.


    Interrupts and traps
    --------------------

    The declaration

    INTERRUPT GLOBAL

    ENDINT

    may be used to declare an interrupt routine, where:

    is the routine name
    (it will also be used to name the associated device)

    is the interrupt vector address of the associated device
    and
    is the console/status register address of the
    associated
    device.

    The parameter is optional (traps and some devices, such as the
    PDP-
    11/03 line-clock, have no control register) as is the parameter GLOBAL.
    The
    parameters , , and GLOBAL may occur in any order.

    and , unlike other parameters in STRUCT, *must be
    integers,
    or symbols defined as integers*.

    Once such a declaration has occurred, the user may call:

    INSTALL

    or, synonymously:



    to load the address of the routine and the parameter into the
    interrupt
    vector position named in the routine declaration. If is absent,
    the
    processor-status part of the interrupt vector is cleared.

    The calls DISABLE and ENABLE respectively disable and
    enable
    interrupts from the device. These are, of course, meaningful only if there
    is
    a control register.


    Example:

    INTERRUPT KEYBD 177560,60 ; These are the addresses of
    ; the console keyboard.
    ; Does whatever is desired with
    ; character typed on console keys.
    ENDINT

    INSTALL KEYBD ; Sets up interrupt vector (ps zero)

    ENABLE KEYBD ; Enables keyboard interrupts

    If the parameter GLOBAL is present, the interrupt routine can be
    INSTALLed,
    and interrupts ENABLEd or DISABLEd from other modules. In each such
    other
    module, there must be an 'external' declaration of the form:

    INTERRUPT EXTERNAL

    The routine declaration must precede in the module any INSTALL, ENABLE,
    or
    DISABLE which refers to it. To allow any of these operations to occur
    earlier
    in the module than the routine body, a 'forward' declaration is provided
    in
    the form:

    INTERRUPT FORWARD

    In these two cases, there is, of course, no code, and no ENDINT.


    Declarations
    ------------

    Declarations take one of the form:

    WORD ,,...
    or
    BYTE ,,...

    where each is either a MACRO-11 identifier or else a doublet or
    triplet
    enclosed in angle brackets; for example:

    WORD X,Y,,Z,

    The first item of each doublet or triplet is a MACRO-11 identifier, the
    others
    are integers (or assembly-time variables).

    The effect of the given declaration is to reserve a word for each of
    X,Y,M,Z,
    initializing M to 777, and an array of 6 words for A, initializing each
    to
    101. After each complete BYTE line, .EVEN is generated to ensure that
    the
    subsequent line begins on a word boundary.


    Warning:

    In the event that variables are used as the second or third items
    of
    doublets or triplets, it must be remembered that it is the
    assembly-
    time value of the variable which is used in the declaration.
    Thus,
    WORD , will initialize M to the address of X, not to
    its
    run-time contents, 22. The usual MACRO-11 restrictions, as to
    whether
    variables must be defined before use, apply here.

    Other forms of declaration are obtained using conventional MACRO-11
    facilities
    (i.e., .ASCII, .BLKW, .WORD, and so on).


    BEGIN and END
    -------------

    Following the definitions of the macros which implement the
    above
    constructions, the program must be preceded by the macro call:

    BEGIN ,,...

    where the optional s are macros in the system library
    file
    SYSMAC.SML. BEGIN initializes certain parameters used in the code
    generation,
    and generates .MCALLS to the macros in the list. The program may also
    be
    terminated by a macro call of the form:

    END

  3. Re: STRUCT79

    Hello, Glen!

    > I used to use macros for the OS/360 assembler that would
    > assemble 8080 and Z80 code. Each macro would expand to the
    > appropriate DC instructions generating the appropriate
    > 8080 or Z80 code.
    >
    > Did anyone ever do something similar with MACRO-11?
    >
    > I wouldn't be surprised if it was done with MACRO-10, as I
    > know the PDP-10/TOPS-10 was popular with early 8080 programmers.


    Well, I write this from memory. When I was working on MITS Altair BASIC, I
    read that Paul Allen was using one such set of macros on a mini. But, of
    course, the details were very, very tiny. Maybe an American fan of Bill
    Gates, having read his books, will know the details? (Else, this week-end, I
    could try to refind my source.)

    (In short, Paul Allen wrote all the interpreter, Bill Gates wrote the I/O
    routines, and Monte Davidoff wrote the FP math package. This is what you can
    read, at the start of the 1975 listing...)

    Yours Sincerely,
    Mr. Emmanuel Roche, France




  4. Re: STRUCT79

    Mr. Emmanuel Roche, France wrote:

    > LEVISON.WS4
    > -----------


    > Technical Report No.79-88


    > - "STRUCT79:
    > A Macro Package to Simulate High-Level Control Structures in MACRO-11"
    > Dr. Michael Levison
    > Department of Computing and Information Science
    > Queen's University, Kingston, Ontario, Canada


    > (C) 1979 M. Levison


    I used to use macros for the OS/360 assembler that would
    assemble 8080 and Z80 code. Each macro would expand to the
    appropriate DC instructions generating the appropriate
    8080 or Z80 code.

    Did anyone ever do something similar with MACRO-11?

    I wouldn't be surprised if it was done with MACRO-10, as I
    know the PDP-10/TOPS-10 was popular with early 8080 programmers.

    -- glen


  5. Re: STRUCT79

    On 2008-09-26, glen herrmannsfeldt wrote:
    > I used to use macros for the OS/360 assembler that would
    > assemble 8080 and Z80 code. Each macro would expand to the
    > appropriate DC instructions generating the appropriate
    > 8080 or Z80 code.
    >
    > Did anyone ever do something similar with MACRO-11?


    I've done similar with MACRO-32, but for assembling microcode rather
    than 8080/z80 assembler.

    I did the microcode for the Firefox QBUS adapter using MACRO32 on a
    MicroVAX 2000. It took that machine half an hour to assemble the
    microcode.

    I'm currently using MACRO32 to maintain microcode for a 2910/29116 based
    controller that I migrated from the META29R assembler. We needed to
    migrate off the VAX, so I moved it to MACRO32 and can now assemble on
    VAX, Alpha, or Itanium. I managed to do it in a manner that required
    very few changes to the original source microcode as well.

    MACRO-32 is very powerful. I really like it.
    --
    roger ivie
    rivie@ridgenet.net