• Fizz Buzz

    From jayjwa@jayjwa@atr2.ath.cx.invalid to alt.sys.pdp10,alt.lang.asm on Thu Jul 23 17:39:22 2026
    From Newsgroup: alt.lang.asm

    As I continue on with MACRO-20, I thought I'd share my Fizz Buzz
    solution for TOPS-10 and TOPS-20. I wanted the solution where you stuff pointers to const char in a buffer, then dump the buffer once at the end
    but I couldn't get it working with PSOUT% on T20. Maybe it's not
    possible. Oddly, T10 won't take TDNE and branch properly so I had to use
    CAIN instead. This works on TOPS-20 (see code and comments). Both
    systems are KL.

    T20 can .cont if you HALTF% and JRST START. How can I restart the
    program in a similar fashion on TOPS-10? I only see EXIT instead of
    HALTF%, which doesn't allow .cont restarting.

    If other alt.lang.asm'ers want to do Fizz Buzz, don't forget to remove
    the pdp10 newsgroup from your reply if it's not about the pdp10.

    .dir fizasm.*
    FIZASM REL 2 <057> 23-Jul-99 DSKB: [30,1,DEV]
    FIZASM EXE 8 <057> 23-Jul-99
    FIZASM MAC 5 <057> 23-Jul-99
    Total of 15 blocks in 3 files on DSKB: [30,1,DEV]

    .run fizasm
    1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fiz
    z 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz
    41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz
    EXIT

    @vdir fizasm.*
    DSK1:<PROGRAMMING>
    FIZASM.EXE.2;P777700 2 1024(36) 22-Jul-2026 21:06:10 JAYJWA
    .MAC.87;P676742 2 2781(7) 22-Jul-2026 21:48:11 JAYJWA
    .REL.82;P777700 1 166(36) 22-Jul-2026 21:48:15 JAYJWA

    Total of 5 pages in 3 files

    @time fizasm
    1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fiz
    z 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz
    41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz
    0.04 real 0.01 user 0.00 sys

    C (KCC) "const char pointers to buffer" version runs about the same.
    @time fizz
    1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fiz
    z 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz
    41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz
    0.04 real 0.01 user 0.00 sys


    comment $
    Fizz Buzz for PDP-10 TOPS-10
    For 1..N, check if each integer is evenly divisible by 3, if so, print
    "Fizz". If by 5, print "Buzz". If by both print "FizzBuzz". If neither,
    print the bare number.
    $
    title fizasm
    search monsym,macsym
    STDAC. ; Labels accums for us

    size==62 ; Number of terms to see (50)
    ; Only <= 99 is supported
    fizz: asciz /Fizz / ; Tag for N mod 3 == 0
    buzz: asciz /Buzz / ; Tag for N mod 5 == 0
    fzbz: asciz /FizzBuzz / ; Tag for N mod 15 == 0
    flags: 0
    crlf: 15B6+12B13 ; Bit-pack a \r\n

    subttl Main
    start: setz cx, ; Zero counter for loop
    st.1: addi cx,1 ; Examine next number
    chk15: move q1,cx ; Number to examine in Q1
    idivi q1,17 ; Div by 15, rem in ac+1
    jumpn q2,chk3 ; N mod 15 != 0, don't print
    movei q3,1 ; Set bit 35 to indicate that
    iorm q3,flags ; we've output a word
    outstr fzbz ; T10 has OUTSTR, not PSOUT%
    jumpa chkn.1 ; to output string to TTY

    chk3: move q1,cx ; Re-get number to examine, Q1
    idivi q1,3 ; Divide by 3, remainder ac+1
    jumpn q2,chk5 ; N mod 3 != 0, don't print
    movei q3,1 ; Set bit 35 to indicate that
    iorm q3,flags ; we've output a word already
    outstr fizz ; Output fizz tag to TTY

    chk5: move q1,cx ; Reload number because IDIVI
    idivi q1,5 ; in CHK3 blanked it
    jumpn q2,chknum ; N mod 5 != 0, don't print
    movei q3,1 ; Ready flags setting
    iorm q3,flags ; Set bit 35, indicating we
    outstr buzz ; printed a tag to TTY
    jumpa chkn.1 ; Printed at least one tag so
    ; skip printing num itself chknum: move q3,flags ; Examine flags
    ; TOPS-10 won't work correctly with TDNE Q3,1 unlike on TOPS-20. Use CAIN
    ; instead to avoid printing a tag and its number. No idea why this is.
    cain q3,1 ; If anything set this then
    jrst chkn.1 ; skip printing the bare num
    move q1,cx ; Fetch number to convert
    jsr numout ; Print bare num

    chkn.1: setzm flags ; Reset flags to zero
    caige cx,size ; More numbers to check?
    jrst st.1 ; Yes (increment above)
    outstr crlf ; No, done, tidy output
    exit
    jrst start ; Does restart work on T10?


    subttl Number Out Subroutine
    ; TOPS-10 has no NOUT% like TOPS-20 does, so convert the system number
    ; by hand to ASCII printing characters for display on the TTY. Only
    ; supports up to double-digit numbers.
    ; Input: Q1 (Q1, Q2 could be overwritten)
    ; Output: Prints ASCII char(s) equal to the number to the TTY
    numout: 0 ; Save return address spc
    idivi q1,12 ; Convert to printable char
    addi q1,60 ; In first position
    caie q1,60 ; Don't print leading zero
    outchr q1 ; Print first character
    addi q2,60 ; Convert second char
    outchr q2 ; And print to TTY
    outchr [.CHSPC] ; Emit space character
    jrst @numout ; Return to caller


    lit ; Debugger to expand literals
    end start ; Assembler is done


    And the TOPS-20 version:
    comment $
    Fizz Buzz for PDP-10 TOPS-20
    For 1..N, check if each integer is evenly divisible by 3, if so, print
    "Fizz". If by 5, print "Buzz". If by both print "FizzBuzz". If neither,
    print the bare number.
    $
    title fizasm
    search monsym,macsym
    STDAC. ; Labels accums for us
    ; cx = iterator for loopage
    ; Set number of terms you want, in octal. Currently 50 decimal
    size==62 ; How far to go with this
    fizz: asciz /Fizz / ; Tag for N mod 3 == 0
    buzz: asciz /Buzz / ; Tag for N mod 5 == 0
    fzbz: asciz /FizzBuzz / ; Tag for N mod 15 == 0
    flags: 0 ; Bit 35 set if word output crlf: 15B6+12B13 ; Bit-pack a \r\n

    start: setz cx, ; Zero counter for loop
    st.1: addi cx,1 ; Examine next number
    chk15: move q1,cx ; The number to examine in Q1
    idivi q1,17 ; Div by 15, rem in ac+1
    jumpn q2,chk3 ; N mod 15 != 0, don't print
    movei q3,1 ; Set bit 35 to indicate that
    iorm q3,flags ; we've output a word
    hrroi t1,fzbz ; Output fzbz tag to TTY
    psout%
    jumpa chkn.1 ; No more printing now

    chk3: move q1,cx ; Re-get number to examine, Q1
    idivi q1,3 ; Divide by 3, remainder ac+1
    jumpn q2,chk5 ; N mod 3 != 0, don't print
    movei q3,1 ; Set bit 35 to indicate that
    iorm q3,flags ; we've output a word
    hrroi t1,fizz
    psout% ; Output fizz tag to TTY

    chk5: move q1,cx ; Reload number because IDIVI
    idivi q1,5 ; in CHK3 blanked it
    jumpn q2,chknum ; N mod 5 != 0, don't print
    movei q3,1
    iorm q3,flags ; Set bit 35, indicate we
    hrroi t1,buzz ; printed a tag
    psout% ; Output buzz tag to TTY
    jumpa chkn.1 ; Printed at least one tag, so
    ; skip printing the num itself chknum: move q3,flags ; Examine flags
    tdne q3,^B1 ; If anything set this then
    jrst chkn.1 ; skip printing the bare num
    move t1,[.priou] ; Else print bare number
    move t2,cx ; Ready NOUT%'s num
    movei t3,12 ; Base is decimal
    nout% ; Print num to TTY
    erjmp [hrroi t1,[asciz /IO error on NOUT%. Exiting./]
    psout%
    haltf%] ; Error? Can't continue, exit
    move t1,[point 7,[asciz / /]] ; Emit space char, tidy output
    psout%

    chkn.1: setzm flags ; Reset flags to zero
    caige cx,size ; More numbers to check?
    jrst st.1 ; Yes (increment above)
    hrroi t1,crlf ; No, done, tidy output
    psout% ; with a \r\n
    haltf% ; Exit to monitor
    jrst start ; Want to see that again?

    lit ; DDT to expand literals
    end start ; Assembler is done
    --
    PGP Key ID: 781C A3E2 C6ED 70A6 B356 7AF5 B510 542E D460 5CAE
    "The Internet should always be the Wild West!"
    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From Rich Alderson@news@alderson.users.panix.com to alt.sys.pdp10,alt.lang.asm on Thu Jul 23 23:19:53 2026
    From Newsgroup: alt.lang.asm

    jayjwa <jayjwa@atr2.ath.cx.invalid> writes:

    As I continue on with MACRO-20, I thought I'd share my Fizz Buzz
    solution for TOPS-10 and TOPS-20. I wanted the solution where you stuff pointers to const char in a buffer, then dump the buffer once at the end
    but I couldn't get it working with PSOUT% on T20. Maybe it's not
    possible. Oddly, T10 won't take TDNE and branch properly so I had to use
    CAIN instead. This works on TOPS-20 (see code and comments). Both
    systems are KL.

    I call bullshit.

    TDNE is a hardware instruction.

    Tops-10 is software.

    There is no way that a hardware instruction is blocked by the operating system. You did something else wrong.

    T20 can .cont if you HALTF% and JRST START. How can I restart the
    program in a similar fashion on TOPS-10? I only see EXIT instead of
    HALTF%, which doesn't allow .cont restarting.

    You don't read manuals very well, do you? I find the following in the
    Monitor Calls Reference manual:

    | 22.46 EXIT [CALLI 12]
    |
    | FUNCTION
    |
    | Stops job execution and optionally resets the job.
    |
    | CALLING SEQUENCE
    |
    | EXIT fcn-code,
    | continue return
    |
    | In the calling sequence, the program supplies the following variables:
    |
    | o fcn-code is one of the function codes described below.
    |
    | For either code, when you EXIT from a job in an auto-pushed
    | context, you are returned to the superior context and the inferior | one is deleted.
    |
    | o continue return is the instruction to be executed if the user
    | issues a valid CONTINUE monitor command.

    Notice that the description of the EXIT call includes a function code. What do you suppose they might mean for the execution of the instruction? Oh, here we go.

    | The function codes and their meanings are:
    |
    | Code Function
    |
    | 0 Performs the following:
    |
    | o Releases all I/O devices, closing files if necessary.
    | o Unlocks the job from core.
    | o Sets the user-mode write-protect bit for the high segment.
    | o Resets APR traps to zero.
    | o Clears PC flags.
    | o Performs a RESET and stops the job.
    |
    | If timesharing was stopped by a TRPSET monitor call, the monitor
    | resumes timesharing. A RESET monitor call is executed, and the
    | word EXIT is typed on your terminal, and the terminal is left in
    | monitor mode. You cannot continue with the CONT or CCONT monitor | command.

    OK, leaving off a function code is equivalent to coding a 0, right?
    Moving on...

    | 1 Performs the following:
    |
    | o Clears PC flags.
    | o Stops the job.
    |
    | EXIT is not printed on your terminal, and you can continue program | execution with the CONT or CCONT monitor command. If you use
    | function code 1, you should first RELEASE all devices and
    | channels; a convenient way to do this is to use the RESET monitor | call. The symbol for EXIT 1, is MONRT.

    Hmm. That looks like exactly what you want, doesn't it?

    | 2-17 Reserved for use by DIGITAL.

    Stingy bastards.

    If other alt.lang.asm'ers want to do Fizz Buzz, don't forget to remove
    the pdp10 newsgroup from your reply if it's not about the pdp10.

    .dir fizasm.*
    FIZASM REL 2 <057> 23-Jul-99 DSKB: [30,1,DEV]
    FIZASM EXE 8 <057> 23-Jul-99
    FIZASM MAC 5 <057> 23-Jul-99
    Total of 15 blocks in 3 files on DSKB: [30,1,DEV]

    .run fizasm
    1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fiz
    z 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz
    41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz
    EXIT

    @vdir fizasm.*
    DSK1:<PROGRAMMING>
    FIZASM.EXE.2;P777700 2 1024(36) 22-Jul-2026 21:06:10 JAYJWA
    .MAC.87;P676742 2 2781(7) 22-Jul-2026 21:48:11 JAYJWA
    .REL.82;P777700 1 166(36) 22-Jul-2026 21:48:15 JAYJWA

    Total of 5 pages in 3 files

    @time fizasm
    1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fiz
    z 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz
    41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz
    0.04 real 0.01 user 0.00 sys

    C (KCC) "const char pointers to buffer" version runs about the same.
    @time fizz
    1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fiz
    z 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz
    41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz
    0.04 real 0.01 user 0.00 sys

    comment $
    Fizz Buzz for PDP-10 TOPS-10
    For 1..N, check if each integer is evenly divisible by 3, if so, print
    "Fizz". If by 5, print "Buzz". If by both print "FizzBuzz". If neither,
    print the bare number.
    $
    | title fizasm
    | search monsym,macsym
    | STDAC. ; Labels accums for us
    |
    | size==62 ; Number of terms to see (50) | ; Only <= 99 is supported
    | fizz: asciz /Fizz / ; Tag for N mod 3 == 0
    | buzz: asciz /Buzz / ; Tag for N mod 5 == 0
    | fzbz: asciz /FizzBuzz / ; Tag for N mod 15 == 0
    | flags: 0
    | crlf: 15B6+12B13 ; Bit-pack a \r\n

    OK, usually we allow the assembler to do the work:

    CRLF: BYTE(7) 15,12f

    or

    CRLF: ASCIZ/
    /

    | subttl Main
    | start: setz cx, ; Zero counter for loop
    | st.1: addi cx,1 ; Examine next number
    | chk15: move q1,cx ; Number to examine in Q1
    | idivi q1,17 ; Div by 15, rem in ac+1
    | jumpn q2,chk3 ; N mod 15 != 0, don't print | movei q3,1 ; Set bit 35 to indicate that | iorm q3,flags ; we've output a word

    Can FLAGS have a value other than 0 or 1? A much better solution in that case is to use 0 and -1, and the SETO/SETZ instructions. Here, use

    SETOM FLAGS

    | outstr fzbz ; T10 has OUTSTR, not PSOUT% | jumpa chkn.1 ; to output string to TTY
    |
    | chk3: move q1,cx ; Re-get number to examine, Q1
    | idivi q1,3 ; Divide by 3, remainder ac+1 | jumpn q2,chk5 ; N mod 3 != 0, don't print
    | movei q3,1 ; Set bit 35 to indicate that | iorm q3,flags ; we've output a word already
    SETOM FLAGS
    | outstr fizz ; Output fizz tag to TTY
    |
    | chk5: move q1,cx ; Reload number because IDIVI | idivi q1,5 ; in CHK3 blanked it
    | jumpn q2,chknum ; N mod 5 != 0, don't print
    | movei q3,1 ; Ready flags setting
    | iorm q3,flags ; Set bit 35, indicating we
    SETOM FLAGS
    | outstr buzz ; printed a tag to TTY
    | jumpa chkn.1 ; Printed at least one tag so | ; skip printing num itself
    | chknum: move q3,flags ; Examine flags
    | ; TOPS-10 won't work correctly with TDNE Q3,1 unlike on TOPS-20. Use CAIN
    | ; instead to avoid printing a tag and its number. No idea why this is.
    | cain q3,1 ; If anything set this then
    | jrst chkn.1 ; skip printing the bare num

    Well, of course TDNE does not work here. READ THE HARDWARE MANUAL.

    The Test Direct instructions compare the contents of the AC to THE CONTENTS OF THE EFFECTIVE ADDRESS, not to the immediate value in the instruction, unlike all the other Test instructions. What you are saying in TDNE Q3,1 is that the processor should bitwise compare the value you've loaded into Q3 WITH THE VALUE CURRENTLY IN AC 1. What would have worked is TDNE Q3,[1] with a literal containing the value 1.

    But even better is using the -1/0 trick from above, and coding

    SKIPN FLAGS
    JRST CHKN.1

    | move q1,cx ; Fetch number to convert
    | jsr numout ; Print bare num

    Worst possible subroutine call choice you can make. It's the 1980s, for crying out loud--use a stack and PUSHJ/POPJ.

    | chkn.1: setzm flags ; Reset flags to zero
    | caige cx,size ; More numbers to check?
    | jrst st.1 ; Yes (increment above)
    | outstr crlf ; No, done, tidy output
    | exit

    And as you've now learned, this should be

    EXIT 1,
    or even better
    MONRT

    | jrst start ; Does restart work on T10?


    | subttl Number Out Subroutine
    | ; TOPS-10 has no NOUT% like TOPS-20 does, so convert the system number
    | ; by hand to ASCII printing characters for display on the TTY. Only
    | ; supports up to double-digit numbers.
    | ; Input: Q1 (Q1, Q2 could be overwritten)
    | ; Output: Prints ASCII char(s) equal to the number to the TTY
    | numout: 0 ; Save return address spc
    | idivi q1,12 ; Convert to printable char
    | addi q1,60 ; In first position
    | caie q1,60 ; Don't print leading zero
    | outchr q1 ; Print first character
    | addi q2,60 ; Convert second char
    | outchr q2 ; And print to TTY
    | outchr [.CHSPC] ; Emit space character
    | jrst @numout ; Return to caller

    <hrrk!>

    | lit ; Debugger to expand literals | end start ; Assembler is done


    | And the TOPS-20 version:
    | comment $
    | Fizz Buzz for PDP-10 TOPS-20
    | For 1..N, check if each integer is evenly divisible by 3, if so, print
    | "Fizz". If by 5, print "Buzz". If by both print "FizzBuzz". If neither,
    | print the bare number.
    | $
    | title fizasm
    | search monsym,macsym
    | STDAC. ; Labels accums for us
    | ; cx = iterator for loopage
    | ; Set number of terms you want, in octal. Currently 50 decimal
    | size==62 ; How far to go with this
    | fizz: asciz /Fizz / ; Tag for N mod 3 == 0
    | buzz: asciz /Buzz / ; Tag for N mod 5 == 0
    | fzbz: asciz /FizzBuzz / ; Tag for N mod 15 == 0
    | flags: 0 ; Bit 35 set if word output
    | crlf: 15B6+12B13 ; Bit-pack a \r\n

    See above.

    | start: setz cx, ; Zero counter for loop
    | st.1: addi cx,1 ; Examine next number
    | chk15: move q1,cx ; The number to examine in Q1 | idivi q1,17 ; Div by 15, rem in ac+1
    | jumpn q2,chk3 ; N mod 15 != 0, don't print | movei q3,1 ; Set bit 35 to indicate that | iorm q3,flags ; we've output a word

    See above.

    | hrroi t1,fzbz ; Output fzbz tag to TTY
    | psout%
    | jumpa chkn.1 ; No more printing now

    It is preferable always to use JRST rather than JUMPA, because the latter invokes the comparison hardware even when the intent is to branch unconditionally.

    | chk3: move q1,cx ; Re-get number to examine, Q1
    | idivi q1,3 ; Divide by 3, remainder ac+1 | jumpn q2,chk5 ; N mod 3 != 0, don't print
    | movei q3,1 ; Set bit 35 to indicate that | iorm q3,flags ; we've output a word

    See above.

    | hrroi t1,fizz
    | psout% ; Output fizz tag to TTY
    |
    | chk5: move q1,cx ; Reload number because IDIVI | idivi q1,5 ; in CHK3 blanked it
    | jumpn q2,chknum ; N mod 5 != 0, don't print
    | movei q3,1
    | iorm q3,flags ; Set bit 35, indicate we

    See above.

    | hrroi t1,buzz ; printed a tag
    | psout% ; Output buzz tag to TTY
    | jumpa chkn.1 ; Printed at least one tag, so
    | ; skip printing the num itself
    | chknum: move q3,flags ; Examine flags
    | tdne q3,^B1 ; If anything set this then
    | jrst chkn.1 ; skip printing the bare num

    OK, I refuse to believe that this worked, just because you declared that the integer 1 was a binary value. Something in AC 1 was ACCIDENTALLY of a shape to work. Singlestep it in DDT and look at the values in T1 and Q3.

    And the same thing is still true as above in the Tops-10 version. Use SKIPE/SKIPN
    and the 0/-1 values.

    | move t1,[.priou] ; Else print bare number

    Usually MOVEI T1,.PRIOU

    | move t2,cx ; Ready NOUT%'s num
    | movei t3,12 ; Base is decimal
    | nout% ; Print num to TTY
    | erjmp [hrroi t1,[asciz /IO error on NOUT%. Exiting./]
    | psout%
    | haltf%] ; Error? Can't continue, exit | move t1,[point 7,[asciz / /]] ; Emit space char, tidy output
    | psout%
    |
    | chkn.1: setzm flags ; Reset flags to zero
    | caige cx,size ; More numbers to check?
    | jrst st.1 ; Yes (increment above)
    | hrroi t1,crlf ; No, done, tidy output
    | psout% ; with a \r\n
    | haltf% ; Exit to monitor
    | jrst start ; Want to see that again?
    |
    | lit ; DDT to expand literals
    | end start ; Assembler is done

    It's nice tht you want to teach folks PDP-10 assembler programming, but don't you think you should do that when you know what you're doing?

    Rich
    --
    Rich Alderson news@alderson.users.panix.com
    Audendum est, et veritas investiganda; quam etiamsi non assequamur,
    omnino tamen proprius, quam nunc sumus, ad eam perveniemus.
    --Galen --- Synchronet 3.22a-Linux NewsLink 1.2