• MACRO-20/Longo text programs

    From jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Thu May 14 17:34:34 2026
    From Newsgroup: alt.lang.asm

    I'm working through the "Introduction To DECSYSTEM-20 Assembly
    Programming" book by Stephen Longo. I have the Gorin one as well but the
    Longo text jumps into writing actual programs faster. There's examples
    that the student is supposed to complete at the end of the
    chapters. There's no given sample output and no answers so the student
    is on his own. I thought I'd post some of my solutions if people want to
    follow along on their PDP-10 with the MACRO-20 assembler.

    @exec ch2u5
    LINK: Loading
    [LNKXCT CH2U5 execution]
    Type in an integer: 30
    Sum: 50
    Dif: 10
    @cont
    Type in an integer: 10
    Sum: 30
    Dif: -10


    ; Chapter 2 user exercise 5
    ; Accept an integer from the terminal. Add the integer to
    ; an integer stored in the program and then subtract it
    ; from the same integer. Assume the integer given is 20.
    title ch2u5
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators
    ac2=2
    ac3=3

    twent: 24 ; The given number 0o24=20 dec
    usrnum: 0 ; Store user's type-in number
    base10: 12 ; 0o12 = 10 decimal base
    ttyin: .priin ; Primary input keyboard
    ttyout: .priou ; Primary output TTY

    msg1: asciz /Type in an integer: / ; Prompt for user data message
    sum: asciz /Sum: / ; Label sum message
    dif: asciz /Dif: / ; Label difference message
    errmsg: asciz /IO Error/ ; Generic error message!

    start: hrroi ac1,msg1 ; Point to prompt message
    psout% ; Print it to TTY

    ; NIN% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Point to keyboard
    move ac3,base10 ; Indicate base 10
    nin% ; Get user integer
    erjmp error ; Handle error
    movem ac2,usrnum ; Save user's num to memory

    ; Do the sum part of the problem now
    hrroi ac1,sum ; Print sum label
    psout% ; To TTY

    ; NOUT% wants device, number, and base in accum 1, 2, 3
    move ac1,ttyout ; Indicate TTY
    move ac2,usrnum ; Get user's number
    add ac2,twent ; Add 20 decimal
    move ac3,base10 ; Indicate base 10
    nout% ; Print sum to TTY
    erjmp error ; Handle error

    movei ac1,15 ; Print a \r
    pbout%
    movei ac1,12 ; Print a \n
    pbout% ; TOPS-20 wants both

    ; Do the difference part of the problem now
    hrroi ac1,dif ; Print diff label
    psout% ; To TTY

    move ac1,ttyout ; Indicate TTY
    move ac2,usrnum ; Retrieve user's num
    sub ac2,twent ; Sub 20 decimal
    move ac3,base10 ; Indicate base 10 dec
    nout% ; Print diff to TTY
    erjmp error ; Handle error
    haltf% ; Done
    jrst start ; Restart prog if desired

    error: hrroi ac1,errmsg ; Indicate error
    psout% ; Print to screen
    haltf% ; Exit
    end start ; That's it
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Fri May 15 14:10:41 2026
    From Newsgroup: alt.lang.asm

    Chapter 3, ch3u1 and ch3u2. I find comparisons to be confusing on this
    CPU. On x86, you use "cmp", the flags are set, and you jump based on the outcome. In MACRO-20, you seem to need to test for the thing you don't
    want, and then continue the execution path from there. If the condition
    fires off, it "eats" next instruction, else it goes on. Very odd. See
    the "check:" label below for an example of what I mean.

    @exec ch3u1
    LINK: Loading
    [LNKXCT CH3U1 execution]
    Enter an integer: 6
    6
    @cont
    Enter an integer: -4
    NEG
    @cont
    Enter an integer: 0
    @

    ; Chapter 3 user exercise 1
    ; Accept an integer via NIN% (the text has a mistake here).
    ; If the int is positive, display it
    ; If the int is negative, print "NEG"
    ; Assumed to do nothing if zero.
    ; CAM = compare accumulator to memory location
    ; CAI = compare accum to immediate value
    ; l, g, e, n = less than, greater, equal, not equal suffixes
    title ch3u1
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators
    ac2=2
    ac3=3

    base: 12 ; 0o12 = 10 decimal
    ttyin: .priin ; Primary input keyboard
    ttyout: .priou ; Primary output to TTY

    prompt: asciz /Enter an integer: / ; User prompt
    negmsg: asciz /NEG / ; Tell user it's negative

    start: hrroi ac1,prompt ; Load user prompt
    psout% ; Print it to TTY

    ; NIN% wants device, radix to 1, 3 and puts results in ac2
    ; Both NIN% and NOUT% require ERJMP, which is like an "else"
    move ac1,ttyin ; Get input from keyboard
    move ac3,base ; Signal what base to get
    nin%
    erjmp done ; Don't worry about error

    ; NIN% will give us an integer, not the ASCII octal for it
    ; Compare against the integer, not the ASCII value
    check: cail ac2,0 ; Is ac2's num less than 0?
    jrst pos ; No/else clause, check +/0

    neg: hrroi ac1,negmsg ; It's negative, load message
    psout% ; and print to TTY
    jumpa done ; This condition is handled

    pos: caig ac2,0 ; Is ac2's num greater than 0?
    jrst done ; Else, it must be 0, goto done

    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac1,ttyout ; Prepare to print positive num
    move ac3,base ; Load desired number base
    nout% ; Output number to TTY
    erjmp done ; Handle error (or not)

    done: haltf% ; Either way we're done
    jrst start ; User can re-run program
    end start ; Tell assember we're done and
    ; also where the prog starts at


    User exercise 2, little caculator (add/sub).

    @exec ch3u2
    LINK: Loading
    [LNKXCT CH3U2 execution]
    Enter an integer: 24
    Enter another integer: 6
    Enter an operation (+ or -): +
    30
    @cont
    Enter an integer: 10
    Enter another integer: 30
    Enter an operation (+ or -): -
    -20
    @cont
    Enter an integer: *
    Error: invalid input or operation
    @

    ; Chapter 3 user exercise 2
    ; Get two integers via NIN%, PBIN% an operation as earlier
    ; in the text (only use + or -). Perform the operation and
    ; output the answer. Give error message if operation is
    ; not + or -.
    title ch3u2
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators
    ac2=2
    ac3=3
    op=7 ; Save user's operation

    int1: 0 ; User's first num
    int2: 0 ; User's second num

    base: 12 ; 0o12 = 10 decimal
    ttyin: .priin ; Keyboard
    ttyout: .priou ; Primary output to TTY
    add: "+" ; Mathmatical operations
    sub: "-"

    ; User prompts and error messages
    getnum: asciz /Enter an integer: /
    getnm2: asciz /Enter another integer: /
    errmsg: asciz /Error: invalid input or operation/
    opmsg: asciz /Enter an operation (+ or -): /

    start: hrroi ac1,getnum ; Load prompt for int1
    psout% ; And print it

    ; NIN% wants device, radix to 1, 3 and puts results in ac2
    ; Both NIN% and NOUT% require ERJMP, which is like an "else"
    move ac1,ttyin ; Prepare to get from kbd
    move ac3,base ; Requested number base
    nin% ; Fetch number to ac2
    erjmp error ; Handle error
    movem ac2,int1 ; Save user's 1st num

    hrroi ac1,getnm2 ; Load prompt for int2
    psout% ; And print it to TTY

    ; NIN% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Prepare to get from kbd
    move ac3,base ; Requested number base
    nin% ; Fetch number to ac2
    erjmp error ; If error
    movem ac2,int2 ; Save second number

    ; Numbers 1 and 2 are fetched. Now get the operation
    hrroi ac1,opmsg ; Prompt for operation
    psout% ; Print to TTY
    pbin% ; Get char, either + or -
    move op,ac1 ; Save it
    pbin% ; Eat \r else stray input
    pbin% ; Eat \n too

    ; Check the operation is a valid one (+ or - only)
    camn op,add ; Is it '+'?
    jrst plus ; It is equal, do plus
    camn op,sub ; Is it '-'?
    jrst minus ; It is equal, do minus
    jumpa error ; Don't care if otherwise

    plus: move ac2,int1 ; Fetch int1
    add ac2,int2 ; Add int2 to it
    jumpa disp ; Ready to display

    minus: move ac2,int1 ; Fetch int1
    sub ac2,int2 ; Subtract it from int1

    ; Display output section. Print the result that we calculated
    ; above; it is in accumulator 2. We're here by jump or fall-thru.
    disp: move ac1,ttyout ; Output device is TTY
    move ac3,base ; Load base
    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    nout% ; Print result, in ac2
    erjmp error ; Handle error
    haltf% ; Else we're finished here
    jrst start ; Re-run/cont the program

    error: hrroi ac1,errmsg ; Load error message
    psout% ; And print to TTY
    haltf% ; Exit to monitor
    end start ; That's it, indicate start
    --
    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 R.Wieser@address@is.invalid to alt.lang.asm on Fri May 15 21:44:03 2026
    From Newsgroup: alt.lang.asm

    jayjwa,

    In MACRO-20, you seem to need to test for the thing you don't
    want, .... If the condition fires off, it "eats" next instruction,
    else it goes on. Very odd.

    Its not as odd as you might think : imagine the possibly eaten instruction
    is a jump. In that case you jump on "the thing you want".

    Yes, its a bit of a mind-trick to think about it that way. :-)

    You might also think of it as "skip next instruction if true". iow, if the "eaten" instruction is again a jump, the program will take that jump if the comparision is not true.

    Ofcourse, nothing stops you from replacing that jump with the (re)setting of
    a flag in a register. :-)

    Regards,
    Rudy Wieser


    --- Synchronet 3.22a-Linux NewsLink 1.2
  • From jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Sat May 16 14:35:40 2026
    From Newsgroup: alt.lang.asm

    ch3u3
    Lots of comparisons in this one. It still seems backwards.
    @exec ch3u3
    LINK: Loading
    [LNKXCT CH3U3 execution]
    Enter a character (A, B to echo, C to quit): A
    A
    Enter a character (A, B to echo, C to quit): B
    B
    Enter a character (A, B to echo, C to quit): D
    Enter a character (A, B to echo, C to quit): F
    Enter a character (A, B to echo, C to quit): C
    Program complete
    @cont
    Enter a character (A, B to echo, C to quit): *
    Enter a character (A, B to echo, C to quit): C
    Program complete
    @

    Last night I found that DDT can display with your symbols already
    inserted, like this:
    @debug ch3u3
    LINK: Loading
    [LNKDEB DDT execution]
    DDT
    ch3u3$:
    start$b $g
    START/ MOVEI ITER,5

    gdb seems so friendly and luxurious after using DDT. The text doesn't
    warn about dangling newlines (actually \r\n in TOPS-10/20) but your
    program will run incorrectly under EXEC but fine under DDT if you don't
    handle them. The text for TOPS-10 mentions them and of course C
    programmers have been bit by them at some point.

    comment $
    Chapter 3 user exercise 3
    PBIN% a character. PBOUT% the character only if it is A or B.
    Place the above in a loop that terminates after the 5th PBIN%
    or if the letter C is entered.
    $
    title ch3u3
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators
    iter=7 ; Iterator for loopage

    char: "X" ; User's character

    ; User prompts and messages
    getchr: asciz /Enter a character (A, B to echo, C to quit): /
    compl: asciz /Program complete/

    start: movei iter,5 ; Init iterator for loops
    loop: hrroi ac1,getchr ; Load prompt
    psout% ; And print to TTY

    pbin% ; Get char to ac1
    movem ac1,char ; Save user's char

    ; Input will be char\r\n, deal with \r\n on input stream. Oddly,
    ; the text does not tell you this and your program will run fine
    ; under DDT while debugging but will fail under EXEC.
    pbin%
    pbin%

    ; Now look at what character we have
    move ac1,char
    cain ac1,101 ; Compare to A (0o101)
    jrst disp ; It's A
    cain ac1,102 ; Compare to B
    jrst disp ; It's B
    cain ac1,103 ; Compare to C
    jrst done ; C - exit loop to done
    jumpa pass ; None of these, no output

    disp: pbout% ; Display char in ac1
    movei ac1,15 ; Load \r
    pbout%
    movei ac1,12 ; Load \n
    pbout% ; End in \r\n

    ; Check loop condition. Do we restart the process?
    pass: subi iter,1 ; Cut 1 from our looper
    jumpn iter,loop ; Not zero? repeat loop

    done: hrroi ac1,compl ; Signal program complete
    psout%
    haltf% ; Exit to monitor
    jrst start ; Restartable program
    end start ; That's it, halt assembler
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Mon May 18 22:40:18 2026
    From Newsgroup: alt.lang.asm

    Two this time, ch3u4 and ch3u5.

    Enter two integers and display the greater.
    @exec ch3u4
    LINK: Loading
    [LNKXCT CH3U4 execution]
    Enter integer number one: -5
    Enter number two: 0
    0 is the greater number.
    @cont
    Enter integer number one: 10
    Enter number two: 20
    20 is the greater number.
    @

    comment $
    Chapter 3 user exercise 4
    Input two integers via NIN%. Output the greater via NOUT%. The
    program should be restartable. The text does not say what to
    do if they are the same so we'll assume they must be different.
    $
    title ch3u4
    search monsym ; Use monitor symbols

    ac1=1 ; Label accumulators
    ac2=2
    ac3=3
    num1=6 ; Use higher accum for nums
    num2=7 ; instead of mem locations

    base: 12 ; 0o12 = 10 decimal
    ttyin: .priin ; Keyboard
    ttyout: .priou ; Primary output to TTY

    ; User prompts and error messages
    entnm1: asciz /Enter integer number one: /
    entnm2: asciz /Enter number two: /
    errmsg: asciz /Error./ ; Very generic
    isgtr: asciz / is the greater number./

    start: hrroi ac1,entnm1 ; Load user prompt
    psout% ; Output to TTY

    ; Get first integer from user
    move ac1,ttyin ; Point to kbd
    move ac3,base ; Load base
    ; NIN% wants device, radix to 1, 3 and puts results in ac2
    nin% ; Get integer
    erjmp error ; Handle error
    move num1,ac2 ; Save integer 1

    ; Get second integer from user
    hrroi ac1,entnm2 ; Load num #2 prompt
    psout% ; Output to TTY

    move ac1,ttyin ; Point to keyboard
    move ac3,base ; Load base
    nin% ; Get integer #2
    erjmp error ; Handle error
    move num2,ac2 ; Save integer 2

    ; Compare integer 1 to integer 2. Save num1 to ac3 before it
    ; gets over written and then put it back
    move ac3,num1 ; Remember num1 because...
    sub num1,num2 ; Cut num2 out of num1
    jumpl num1,gtr2 ; Negative? num2 > num1
    jumpg num1,gtr1 ; Positive? num1 > num2
    jumpa done ; Neither (num1=num2)

    gtr2: move ac2,num2 ; Load num2 for NOUT%
    jumpa disp ; Display it

    gtr1: move ac2,ac3 ; Fetch num1 saved from above

    disp: move ac1,ttyout ; Point to TTY
    move ac3,base ; Indicate base
    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    nout% ; ac2 already loaded
    erjmp error ; Handle error else print
    hrroi ac1,isgtr ; Load "greater" message
    psout% ; Print to TTY

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program

    error: hrroi ac1,errmsg ; Load error message
    psout% ; Print
    haltf% ; Exit to monitor
    end start ; That's it


    Enter 5 integers and note the position of the greatest.

    These comparisons still don't make any sense but I can just run them
    with DDT and flip them to the condition I don't want so I get the
    condition that I don't didn't want.

    @exec ch3u5
    LINK: Loading
    [LNKXCT CH3U5 execution]
    Enter number 1:10
    Enter number 2:20
    Enter number 3:2
    Enter number 4:15
    Enter number 5:8
    The largest integer is at position 2
    @cont
    Enter number 1:10
    Enter number 2:20
    Enter number 3:50
    Enter number 4:34
    Enter number 5:60
    The largest integer is at position 5
    @

    comment $
    Chapter 3 user exercise 5
    Set up a loop, accept five integers. Use NOUT% to display the
    position of the largest integer after they have been entered.
    $

    title ch3u5
    search monsym ; Use monitor symbols

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3
    gtrpos=4 ; Position of greatest int
    num=5 ; Number being looked at
    pos=6 ; Position of entry
    iter=7 ; Iterator for loopage

    base: 12 ; 0o12 = 10 decimal
    ttyin: .priin ; Keyboard
    ttyout: .priou ; Primary output to TTY

    ; User prompts and messages
    etrmsg: asciz /Enter number /
    errmsg: asciz /IO error/ ; Generic error message
    lrgmsg: asciz /The largest integer is at position /

    start: xor num,num ; Zero out number
    movei pos,1 ; and init position to first
    movei iter,5 ; Do 5 loops, init looper

    getnum: hrroi ac1,etrmsg ; Load get number prompt
    psout% ; And print

    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac1,ttyout ; Print to TTY position num
    move ac3,base ; Load base
    move ac2,pos ; The numerical position
    nout% ; And print it
    erjmp error ; Handle error
    movei ac1,72 ; To print ":"
    pbout% ; To TTY

    ; nin% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Point to keyboard
    move ac3,base ; Load base
    nin% ; Get integer to ac2
    erjmp error ; Handle error

    ; Examine number we just got that's in ac2. We have to test for the
    ; case we *do not* want, because the opcodes are backward (???)
    caml num,ac2 ; Is it > what we have?
    jrst lesser ; No, jump

    move num,ac2 ; This is greater, note it
    move gtrpos,pos ; And its position

    lesser: addi pos,1 ; Go to next position
    subi iter,1 ; Decrement loop counter
    jumpn iter,getnum ; Not 0? Get next number

    ; By now we've the position of the greatest integer. Display it
    disp: hrroi ac1,lrgmsg ; Load "largest" message
    psout% ; Print to TTY
    move ac1,ttyout ; Print to TTY position num
    move ac3,base ; Load base
    move ac2,gtrpos ; Pos of greatest integer
    nout% ; And print it
    erjmp error ; Handle error

    haltf% ; Exit to monitor
    jrst start ; Restartable program

    error: hrroi ac1,errmsg ; Print error message
    psout% ; To TTY
    haltf% ; Exit to monitor level
    end start ; That's it


    One more user exercise in chapter 3 and I'm on to chapter 4.
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Tue May 19 22:55:48 2026
    From Newsgroup: alt.lang.asm

    ch3u6

    Check positive integers for even/odd without using division.
    @exec ch3u6
    MACRO: ch3u6
    LINK: Loading
    [LNKXCT CH3U6 execution]
    Enter a positive integer to check for even or odd: 0
    @cont
    Enter a positive integer to check for even or odd: 3
    The number is odd.
    @cont
    Enter a positive integer to check for even or odd: 120
    The number is even.
    @cont
    Enter a positive integer to check for even or odd: 121
    The number is odd.
    @

    comment $
    Chapter 3 user exercise 6
    Accept an integer via NIN%. Considering only positive integers,
    PSOUT% a phrase stating whether the integer is EVEN or ODD.
    Use repeated subtractions of 2 to achieve this.
    $
    title ch3u6
    search monsym ; Use monitor's symbols

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3
    num=4 ; The user's integer

    base: 12 ; 10 decimal
    ttyin: .priin ; Keyboard is primary input
    ttyout: .priout ; Output to TTY

    ; User promps and messages
    etrmsg: asciz /Enter a positive integer to check for even or odd: /
    numeve: asciz /The number is even./
    numodd: asciz /The number is odd./
    errmsg: asciz /Error invalid input./

    start: hrroi ac1,etrmsg ; Load get num prompt
    psout% ; Print to TTY

    ; nin% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Point to keyboard
    move ac3,base ; Indicate base
    nin% ; Get integer to ac2
    erjmp error ; Handle error
    move num,ac2 ; Save user's number

    ; The text says "consider only positive integers" but having
    ; the program return "odd" for zero just seems wrong so error
    ; check that the number is actually positive before we act
    jumple num,done ; Exit early if <= 0

    ; Keep subtracting 2 from the user's number. If we hit zero, it
    ; is even, if we go below zero, it is odd. Keep looping if it's
    ; greater than zero until one of the above conditions occurs.
    loop: subi num,2 ; Cut 2 from number
    jumpg num,loop ; It's still greater than 0
    jumpl num,odd ; It's negative, thus odd

    even: hrroi ac1,numeve ; Fall-thru, num even
    psout% ; Print to TTY
    jumpa done ; Already indicated "even"

    odd: hrroi ac1,numodd ; Load "odd" message
    psout% ; Print to TTY

    done: haltf% ; Exit to monitor level
    jrst start ; Program is restartable

    error: hrroi ac1,errmsg ; Load error prompt
    psout% ; And print to TTY
    haltf% ; Exit program to monitor
    end start ; Tell assembler it's 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Fri May 22 18:38:07 2026
    From Newsgroup: alt.lang.asm

    ch4u3 and ch4u4. Exercises 1 and 2 didn't really require
    programming. This chapter deals with "arrays", indexing, and address
    modes. 1 of the modes seems pretty exotic, two simple, and 1 I can use
    if I think about it.

    @exec ch4u3
    LINK: Loading
    [LNKXCT CH4U3 execution]
    10 plus 20 using direct addressing: 30
    10+20 using immediate addressing: 30
    10+20 using indirect addressing: 30
    The sum of 10 and 20 using index addressing: 30
    @

    comment $
    Chapter 4 user exercise 3
    Write a program that will add the same two integers four times
    using a different addressing move for each time.
    Exercise 1 does not require a program, but is actually answered
    by doing this exercise and exercise #2 is a flow char that's
    already drawn in the text.
    $
    title ch4u3
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accums
    ac2=2
    ac3=3

    list: num1 ; Label location of first num
    num1: 12 ; 10 decimal
    num2: 24 ; 20 decimal

    base: 12 ; 10 decimal
    ttyin: .priin ; Keyboard is primary input
    ttyout: .priout ; Output to TTY

    ; Prompts and user messages. Most of these have embedded \r\n
    dirmsg: asciz /10 plus 20 using direct addressing: /
    immmsg: asciz /
    10+20 using immediate addressing: /
    indmsg: asciz /
    10+20 using indirect addressing: /
    idxmsg: asciz /
    The sum of 10 and 20 using index addressing: /

    start: hrroi ac1,dirmsg ; Load direct prompt
    psout% ; Print to TTY
    move ac2,num1 ; Direct to ac2
    add ac2,num2 ; Add num2 to ac2
    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac1,ttyout ; Indicate dest is TTY
    move ac3,base ; Indicate base
    nout% ; Print to TTY
    erjmp error ; Handle error

    hrroi ac1,immmsg ; Load immediate message
    psout%
    movei ac2,12 ; Immediate 10 to ac2
    addi ac2,24 ; Add 20 immed to ac2
    move ac1,ttyout ; Indicate dest is TTY
    move ac3,base ; Indicate base
    nout% ; Print sum to TTY
    erjmp error ; Handle error

    hrroi ac1,indmsg ; Load "indirect" message
    psout% ; Print to TTY
    movei ac2,@list+1 ; Load 10 decimal
    addi ac2,@list+2 ; Add next decimal number
    move ac1,ttyout ; Point to TTY for output
    move ac3,base ; Indicate base 10
    nout% ; Print sum TTY
    erjmp error ; Just exit on error

    hrroi ac1,idxmsg ; Load "index" message
    psout% ; And print it
    movei ac3,1 ; Set up index (1) to ac3
    move ac2,list(ac3) ; Move list+1
    addi ac3,1 ; Set index (2) now
    add ac2,list(ac3) ; Add to sum list+2
    move ac1,ttyout ; Ready to print to TTY
    move ac3,base ; Put back base (overwrote it)
    nout% ; Number was alread in ac2
    erjmp error ; Handle error (or not)

    error: haltf% ; Error or not, done
    jrst start ; Restartable program
    end start ; Assembler's work is done



    This next one is basically a FOR loop.

    @exec ch4u4
    LINK: Loading
    [LNKXCT CH4U4 execution]
    The sum is 15
    @

    comment $
    Chapter 4 user exercise 4
    Code the following high-level language notation into assembly
    language:

    FOR I = I TO 3
    SUM = SUM + A(I)
    NEXT I
    PRINT SUM

    Assume the array A contains 4, 2, 9.
    $
    title ch4u4
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators
    sum=2 ; Tally array sum here
    ac3=3
    iter=4 ; Iterator for loopage

    base: 12 ; 10 decimal
    ttyin: .priin ; Keyboard is primary input
    ttyout: .priout ; Output to TTY
    A: exp 4, 2, 9 ; Our array/list, idx 0-2

    start: xor sum,sum ; Clear sum
    xor iter,iter ; Init iter, loop 0-2
    loop: add sum,A(iter) ; Add element at A(iter)
    addi iter,1 ; Increment loop counter
    caie iter,3 ; Did we do ALL list yet?
    jrst loop ; No, re-loop

    ; If we're here it's time to print the total sum. hrroi can
    ; take a message string directly like so:
    hrroi ac1,[ asciz /The sum is /]
    psout% ; And print

    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac1,ttyout ; Indicate TTY is dest
    move ac3,base ; Indicate base 10 dec
    nout% ; sum should still be in ac2
    erjmp done ; Handle error (or not)

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    end start ; Tell assembler it's done


    Something odd happens with DDT: I label the accumulators, but there's no distinction between the accumulator's label that I gave it and a
    plain-jane value that occurs. DDT does not differentiate. For example,
    above in the "loop:" area, I compare to *values* but the debugger labels
    those values with what I assigned to accumulators, like this:

    LOOP+1/ ADDI ITER,AC1 $x
    ITER/ SUM AC1
    LOOP+2/ CAIE ITER,AC3

    This is the code that made that:
    addi iter,1
    caie iter,3

    In other words, in the above example, DDT should have left the "1" and
    the "3" as is, as the values they are. I'm not sure if this can be
    fixed, or how. Had I an instructor I'd certainly ask him because, in
    that context, I'm refering to numerical values and not accumulators.
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Tue May 26 20:46:12 2026
    From Newsgroup: alt.lang.asm

    ch4u5

    I must remember to use an accumulator for indexing, else I would
    scribble into some other memory. I'm guessing there's no equal of a
    "segfault" on PDP-10? Once I overwrote part of premsg but the program
    still ran. On MS-DOS you could scribble all over, too.

    Reverse parts of an array, and I'm onto chapter 5. In the previous
    example, I wrote array A: as 4, 2, 9. Of course there's no "9" in octal
    but the assembler assumed it was decimal and the program ran anyway. It
    should be as below in this example.

    @exec ch4u5
    LINK: Loading
    [LNKXCT CH4U5 execution]
    The array before swapping: 429
    The array after swapping: 492

    comment $
    Chapter 4 user exercise 5
    Write a program that will interchange the second and
    third elements in the array 4, 2, 9 (decimal) and print
    the new array. The program should operate as this high-level
    example:

    DUMMY = A(I)
    A(I) = A(I + 1)
    A(I + 1) = DUMMY
    $
    title ch4u5
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators
    ac2=2
    ac3=3
    iter=4 ; Iterator for looping loopage
    tmp=5 ; Temp swap space
    idx=6 ; Indexer

    base: 12 ; 10 decimal
    ttyout: .priout ; Primary output to TTY
    A: exp 4, 2, 11 ; Array in octal (4,2,9)

    ; User messages and carriage-return
    premsg: asciz /The array before swapping: /
    posmsg: asciz /The array after swapping: /
    cr: asciz /
    /

    start: hrroi ac1,premsg ; Load pre-swapping message
    psout% ; And print it

    ; Print the array as-is. This will be without commas or spaces
    ; but that's fine for an example execise. Note indexers MUST
    ; be IN accumulators.

    xor iter,iter ; Clear iterator
    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    loop1: move ac1,ttyout ; Indicate dest is TTY
    move ac2,A(iter) ; Load number from array A
    move ac3,base ; Indicate base
    nout% ; Print it
    erjmp error ; Handle error

    addi iter,1 ; Increment loop counter
    caie iter,3 ; Time to stop or not?
    jrst loop1 ; No it is not

    hrroi ac1,cr ; Print a \r\n
    psout%

    ; Swap the array elements by moving the contents of the memory to
    ; a temp accumulator and then moving that into its new spot in A()
    ; The second element is index 1 and the third is index 2. No other
    ; elements need swapping according to the directions. Indexers for
    ; MOVEM MUST be IN accumulators. Using 1, 2, etc directly does not work.
    swap: movei tmp,@A+1 ; Load 2nd element to tmp location
    movei ac1,@A+2 ; Load 3rd element to temp location
    movei idx,1 ; Indicate we want index pos 1
    movem ac1,A(idx) ; Overwrite 2nd element with 3rd
    movei idx,2 ; Indeicate we want index pos 2
    movem tmp,A(idx) ; Overwrite 3rd element with 2nd

    ; Print out revised array
    hrroi ac1,posmsg ; Load "post" message
    psout% ; Print to TTY

    xor iter,iter ; Clear iterator
    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    loop2: move ac1,ttyout ; Indicate dest is TTY
    move ac2,A(iter) ; Load number from array A
    move ac3,base ; Indicate base
    nout% ; Print it
    erjmp error ; Handle error

    addi iter,1 ; Increment loop counter
    caie iter,3 ; Time to stop or not?
    jrst loop2 ; No it is not

    hrroi ac1,cr ; Print a \r\n
    psout% ; To TTY because we're done
    haltf% ; Done
    jrst start ; Restartable program

    error: haltf% ; Some error occured
    end start ; Tell assembler we're 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Thu Jun 4 16:43:35 2026
    From Newsgroup: alt.lang.asm

    ch5u6

    Make a table with A..Z and hex equal.

    There's a small error in the text, explained below. This is the end of
    chapter 5.

    @exec ch5u6
    LINK: Loading
    [LNKXCT CH5U6 execution]
    A 41
    B 42
    C 43
    ...
    X 58
    Y 59
    Z 5A
    @

    comment $
    Write a table to that will make an ASCII table of capital letters
    and their hexadecimal values, A..Z. The text has an error in that
    it labels A..Z as 0x41..0x7A when really it is 0x41..0x5A. 'z'
    (lower case) is 0x7A, not 'Z' (capital letter).
    $
    title ch5u6
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators
    ac2=2
    ac3=3
    iter=4 ; Loop iterator for loopage

    base: 20 ; Base 16 in octal
    ttyout: .priout ; Output to TTY
    tab: 11 ; ASCII tab char in octal
    cr: asciz /
    / ; \r and \n together

    start: movei iter,"A" ; Init to "A". Need double quotes. prtchr: move ac1,iter ; Read to print via PBOUT%
    pbout% ; Send character
    move ac1,tab ; Read to send \t
    pbout% ; Send character

    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac1,ttyout ; Point to TTY
    move ac2,iter ; Load num for printing hex
    move ac3,base ; Base hexadecimal
    nout% ; Print character in hex
    erjmp done ; Error? Just exit.

    hrroi ac1,cr ; Print \r\n after one complete line
    psout% ; Ready for next line

    cail iter,"Z" ; Is it still less than "Z"?
    jrst done ; Yes, "eat" done. Did I mention
    ; how asinine PDP-10 compares are?
    addi iter,1 ; Increment character
    jumpa prtchr ; Do printing loop again

    done: haltf% ; End program
    jrst start ; Restartable program
    end start ; Tell assembler we're 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Fri Jun 12 17:14:45 2026
    From Newsgroup: alt.lang.asm

    Disregarding appendices, Chapter 6 is almost 40% of the book. Why they
    packed so much into this chapter I don't know; going will be slow. On
    top of that there's a dozen or so new opcodes and it's all those logic/shift/rotate/bit commands that make you wonder why you started
    into assembly in the first place.

    ch6u1 - make alphabet chars lowercase
    ch6u2 - make alphabet chars uppercase (as modified)

    There's an issue with ch6u2 because, while it looks like the mirror of
    ch6u1, it specifies bit 29 while likely they meant bit 30. My solution
    has both. This wouldn't be the first time there was a discrepancy in the
    text versus the example code.

    @exec ch6u1
    LINK: Loading
    [LNKXCT CH6U1 execution]
    Enter a single character: a
    a
    @cont
    Enter a single character: B
    b
    @cont
    Enter a single character: 6
    6

    @exec ch6u2
    LINK: Loading
    [LNKXCT CH6U2 execution]
    Enter a single character: A
    A
    @cont
    Enter a single character: b
    B
    @cont
    Enter a single character: 8
    ruA
    @cont
    Enter a single character: 3

    comment $
    PBIN% a character, set bit 30 and print it via PBOUT% What
    happens when you PBIN% a capital letter? A digit?
    $
    title ch6u1
    search monsym

    ac1=1 ; Label used accumulators

    bitmsk: ^B100000 ; Mask to set bit 30, equal 32 decimal
    getmsg: asciz /Enter a single character: / ; User prompt message

    start: hrroi ac1,getmsg ; Load user prompt message
    psout% ; Print to TTY
    pbin% ; Get the character
    ior ac1,bitmsk ; Set bit in bitmsk
    pbout% ; Print it
    pbin% ; Eat \r
    pbin% ; Eat \n
    haltf% ; Exit to monitor
    jrst start ; Restart program
    end start ; Tell assembler that's all



    comment $
    Write a program that will PBIN%. After the character is accepted,
    clear bit 29 and PBOUT% the result. What happens when you PBIN%
    a capital letter? A digit?

    Note the text says "bit 29" but, following the previous exercise,
    it might actually mean "bit 30". Clearing bit 29 shifts on the
    ASCII table, but clearing bit 30 does the opposite of the previous
    exercise (but does not output ASCII numbers). Here I assume it
    wants "clear bit 30" since it makes more sense to mirror exercise
    ch6u1.
    $
    title ch6u2
    search monsym

    ac1=1 ; Label accumulators used

    getmsg: asciz /Enter a single character: / ; User input prompt
    mask1: ^B0100000 ; Mask for bit 30 set
    mask2: ^B1000000 ; Mask for bit 29 set

    start: hrroi ac1,getmsg ; Prompt user for character
    psout% ; Print to TTY
    pbin% ; Get char from keyboard
    ; Use 'mask1' for how I think the text meant the exercise, or 'mask2'
    ; for how the text is written verbatum.
    andcm ac1,mask1 ; Clear bit according to mask
    pbout% ; Print new value
    pbin% ; Eat dangling \r
    pbin% ; and also \n
    haltf% ; Exit to monitor
    jrst start ; Restartable program
    end start ; End assembly
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Mon Jun 15 15:44:47 2026
    From Newsgroup: alt.lang.asm

    ch6u3
    This one took some thinking. Examine each bit in a user-entered integer
    and print HIGH or LOW for each. My solution prints the original digit as
    binary and then each number after (so that I could verify that it
    worked). I examine the integer from the LSB side. The exercise does not
    specify which direction to go in. Rigth-to-left was easiest.

    @exec ch6u3
    LINK: Loading
    [LNKXCT CH6U3 execution]
    Enter a decimal integer to examine: 54
    110110
    LOW
    11011
    HIGH
    1101
    HIGH
    110
    LOW
    11
    HIGH
    1
    HIGH
    @cont
    Enter a decimal integer to examine: 2
    10
    LOW
    1
    HIGH
    @cont
    Enter a decimal integer to examine: 0
    0
    LOW

    comment $
    Accept an integer in base 10. Examine each bit. If set, print the
    word HIGH, followed by a CR. If not set, print LOW and a carriage
    return (example of parallel-to-serial conversion).
    $
    title ch6u3
    search monsym ; Use monitor's symbols

    ac1=1 ; Label accumulators used
    ac2=2
    ac3=3
    ac4=4

    ttyin: .priin ; Primary input is keyboard
    ttyout: .priout ; Primary output to TTY
    base: 12 ; Input base, 10 decimal
    mask: ^B1 ; Compare with LSB for HIGH

    getmsg: asciz /Enter a decimal integer to examine: / ; User prompt
    himsg: asciz /HIGH
    /
    lomsg: asciz /LOW
    / ; Announce high or low for bits msg

    start: hrroi ac1,getmsg ; Load prompt for getting integer
    psout% ; Print to TTY

    ; nin% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Indicate keyboard is input
    move ac3,base ; Request base 10 decimal
    nin% ; Get number
    erjmp done ; Can't continue if no number

    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    strip: move ac1,ttyout ; Print to TTY
    movei ac3,2 ; Indicate binary base
    nout% ; Output number
    erjmp done ; Just exit on error
    movei ac1,15 ; Print \r\n
    pbout%
    movei ac1,12
    pbout%
    move ac4,ac2 ; Don't clobber ac2 - we need it
    and ac4,mask ; Test current LSB
    skipe ac4 ; Is it zero?
    jrst high ; No, it's one

    low: hrroi ac1,lomsg ; Load "low" message
    psout% ; Print to TTY
    jumpa next ; One message per condition

    high: hrroi ac1,himsg ; Load "high" message
    psout% ; Print it; fall-thru to next LSB

    next: lsh ac2,-1 ; Logical shift right 1: look at LSB
    jumpn ac2,strip ; Num not zero yet? Strip more bits

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    end start ; Assembly has concluded
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Tue Jun 16 20:56:37 2026
    From Newsgroup: alt.lang.asm

    ch6u4

    More bit testing programs. The text didn't mention IDIVI but I knew it
    from the TOPS-10 assembly boot. Thankfully I didn't have to use
    subtraction to do division/mod. Still no way mentioned to clear the
    input buffer. There's got to be a better way than calling PBIN% twice.

    Input a character, count bits, and set parity (see comment below at top
    of program). I'm going to try out the FAIL assember next. So far this
    has been MACRO-20.

    @exec ch6u4
    LINK: Loading
    [LNKXCT CH6U4 execution]
    Enter a character to check for parity: a
    1100001
    Number of bits set: 3
    Odd, setting bit 28
    11100001
    @cont
    Enter a character to check for parity: c
    1100011
    Number of bits set: 4
    Even, clearing bit 28
    1100011
    @cont
    Enter a character to check for parity: #
    100011
    Number of bits set: 3
    Odd, setting bit 28
    10100011
    @

    There's no answer key, but that looks correct because '#' = 35 ASCII
    decimal which is

    Python 3.12.13 (main, Mar 3 2026, 15:06:31) [GCC 15.2.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    print( bin( 35 ) )
    0b100011


    comment $
    PBIN% a character. Count the number of bits set in the word. If the number
    of bits is odd, set bit 28. If the number is even, clear 28. This program illustrates even parity - the parity bit is available for checking errors
    when transferring information over phone lines.
    $
    title ch6u4
    search monsym

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3
    ac4=4 ; Keep track of each bit to test
    count=5 ; Track number of set bits
    rem=6 ; Remainder for division

    ttyout: .priout ; Primary output to TTY
    getnum: asciz /Enter a character to check for parity: /
    bitset: asciz /Number of bits set: / ; User prompts and messages
    crlf: 15B6+12B13 ; Bit-pack a \r\n
    b28msk: ^B10000000 ; Bit 28 mask
    tstmsk: ^B1 ; Test a bit mask
    char: block 1 ; Save input char for prg end
    base10: 12 ; 10 in decimal - display base
    base2: 2 ; Base to display char in so
    ; so we can verify which bits set
    subttl input

    start: hrroi ac1,getnum ; Load user prompt message
    psout% ; Print to TTY
    pbin% ; Get character to ac1
    movem ac1,char ; And save for later

    subttl calculate

    setz count, ; Clear count (total)
    more: move ac4,ac1 ; Don't clobber ac1 - we need it
    and ac4,tstmsk ; Test current LSB
    skipe ac4 ; Is it zero?
    addi count,1 ; No, count it in total

    lsh ac1,-1 ; Look at next LSB
    jumpn ac1,more ; More bits to look at?

    subttl output

    ; By now, we have total number of set bits in 'count' and the
    ; original character saved to 'char'.
    pbin% ; Clear input buffer. Eat \r
    pbin% ; and \n

    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac2,char ; Reload original character
    move ac1,ttyout ; Point to TTY device
    move ac3,base2 ; Indicate binary display
    nout% ; Output original char as bits
    erjmp done ; Handle error

    hrroi ac1,crlf ; Emit newline
    psout%
    hrroi ac1,bitset ; Load "bits set" message
    psout% ; Print to TTY

    move ac1,ttyout ; Print to TTY the
    move ac2,count ; number of bits that are set
    move ac3,base10 ; Print number in decimal
    nout% ; Output to TTY
    erjmp done ; Done either way
    hrroi ac1,crlf ; Emit newline
    psout%

    ; Now we look at 'count' and see if it's even or odd. Use mod for this.
    div: idivi count,2 ; Divide 2 and check remainder
    jumpe rem,even ; No remainder? It's even

    odd: hrroi ac1,[asciz /Odd, setting bit 28
    /] ; Else it's odd
    psout% ; Print to TTY
    move ac1,char ; Fetch original character
    ior ac1,b28msk ; At odd, set bit 28
    jumpa endmsk ; Skip "even" code block

    even: hrroi ac1,[asciz /Even, clearing bit 28
    /] ; Note even bit number
    psout%
    move ac1,char ; Fetch orginal character
    andcm ac1,b28msk ; At even, clear bit 28

    endmsk: move ac2,ac1 ; Display final bits to show
    move ac1,ttyout ; the results of masking
    move ac3,base2 ; As bits
    nout% ; Output to TTY
    erjmp done ; Handle error (or not)

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    end start ; Assembly is finished
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Wed Jun 17 18:00:00 2026
    From Newsgroup: alt.lang.asm

    ch6u5.fai

    This is the FAIL assembler. It seems to be a drop-in replacement - at
    least for the programming that I'm doing. This program requires to check
    a user-input number, look at bit 30, then output bits 30-35 on a special condition (see comment section).

    @exec ch6u5
    <PROGRAMMING>CH6U5.FAI.6
    FAIL: ch6u5
    LINK: Loading
    [LNKXCT CH6U5 execution]
    Enter a number to check bits 30-35: 1230
    Your number in bits is 10011001110
    @cont
    Enter a number to check bits 30-35: 1454
    Your number in bits is 10110101110
    30-bit is set
    New number in octal: 56
    @cont
    Enter a number to check bits 30-35: 1455
    Your number in bits is 10110101111
    30-bit is set
    New number in octal: 57

    comment $
    Write a program that accepts a number from the terminal. If the number
    sets bit 30, then treat bits 30 to 35 as an octal number and display
    this value (I am assuming 30-35 inclusive).
    This is the 6 right-most bits: 10110101110
    $
    title ch6u5
    search monsym ; Use monitor's symbols

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3

    ttyout: .priout ; TTY/keyboard input/outputs
    ttyin: .priin
    mask: ^B111111 ; Mask for bits 30-35 inclusive
    mask30: ^B100000 ; Mask for evaluating bit 30
    base10: 12 ; Input base
    base8: 10 ; Output base
    base2: 2 ; Output base for user bits num
    crlf: 15B6+12B13 ; Bit-pack a \r\n
    char: block 1 ; Save orginal number
    getnum: asciz /Enter a number to check bits 30-35: /
    bitmsg: asciz /Your number in bits is /
    bitset: asciz /
    30-bit is set / ; User prompts and messages
    newchr: asciz /
    New number in octal: /

    start: hrroi ac1,getnum ; Load user prompt
    psout% ; Display to TTY

    subttl input

    ; nin% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Get input from keyboard
    move ac3,base10 ; User integer is in decimal
    nin% ; Get number
    erjmp done ; Or error if can't

    movem ac2,char ; Save original number, need later
    hrroi ac1,bitmsg ; Load "bit message"
    psout% ; Display it
    move ac1,ttyout ; Display num to TTY
    move ac3,base2 ; Display as bit string
    nout% ; Print it
    erjmp done ; Or error if can't

    and ac2,mask30 ; Match num against 30bit
    jumpe ac2,done ; Zero? Not a special number

    subttl output

    set: hrroi ac1,bitset ; Signal bit 30 set
    psout% ; Print to TTY
    move ac2,char ; Fetch original number
    and ac2,mask ; Mask off bits 30-35
    hrroi ac1,newchr ; Load new char message
    psout% ; Display to TTY
    move ac1,ttyout ; ditto
    move ac3,base8 ; Output as octal
    nout% ; Send ac2
    erjmp done ; Exit either way

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    end start ; Tell assembler it's 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Thu Jun 18 13:59:24 2026
    From Newsgroup: alt.lang.asm

    ch6u6.mac

    This program wants you prove that two bit strings entered will be:
    char1 XOR char2 = ( (NOT char1) AND char2 )
    IOR ( char1 AND (NOT char2) )

    The confusing part is that the Longo text speaks about NOT as if it is
    an opcode. It's not (pun not not intended). The display font for it in
    the text even matches XOR and AND. The Gorin text does mention an opcode
    to get a complement, but it's SETCM.

    @exec ch6u6
    LINK: Loading
    [LNKXCT CH6U6 execution]
    Enter binary string one: 10101
    Enter binary string two: 101
    The results using AND, IOR, NOT: 10000
    The results using only XOR: 10000
    @cont
    Enter binary string one: 1010
    Enter binary string two: 111
    The results using AND, IOR, NOT: 1101
    The results using only XOR: 1101

    comment $
    Accept two binary words using NIN% base 2. Perform AND, IOR, and NOT
    operations yielding the XOR of the words. Display (NOUT%) the results
    using base 2. Using the same words, XOR them and display the results
    to see they are the same as the previous calculations.
    There's no "NOT" opcode. SETCM is what you're likely supposed to
    use.
    $
    title ch6u6
    search monsym ; Use monitor's symbols

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3

    char1: block 1 ; Storage for the first word
    char2: block 1 ; and the second word
    ttyout: .priout ; TTY/keyboard input/outputs
    ttyin: .priin
    base2: 2 ; Output base for user bits num
    get1: asciz /Enter binary string one: / ; User prompts and messages get2: asciz /Enter binary string two: /
    andres: asciz /The results using AND, IOR, NOT: /
    xorres: asciz /
    The results using only XOR: /

    start: hrroi ac1,get1 ; Load 'string one' message
    psout% ; Print it

    subttl input
    ; nin% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Get from keyboard
    move ac3,base2 ; base 2
    nin% ; the number
    erjmp done ; Handle error (or not)
    setam ac2,char1 ; Save number 1

    hrroi ac1,get2 ; Load 'string two' message
    psout% ; and print it
    move ac1,ttyin ; Get from keyboard
    move ac3,base2 ; base 2
    nin% ; the number
    erjmp done ; Handle error (or not)
    setam ac2,char2 ; Save number 2

    subttl calculate and output
    hrroi ac1,andres ; Print 'and' result before we
    psout% ; need to use those accumulators

    ; How nice of the text to give us the algorithm for this. We have plenty
    ; of registers on the PDP-10, let's use them so as not to clobber memory
    ; because we need the orginal values later for the XOR part of this.
    ; char1 XOR char2 = ( (NOT char1) AND char2 ) IOR ( char1 AND (NOT char2) )
    setcm 4,char1 ; Complement mem at char1
    move 5,char2 ; Fetch char2 from memory
    and 4,5 ; First part is done, in 4
    move 6,char1 ; Fetch char1 from memory
    setcm 7,char2 ; Complement mem at char2
    and 6,7 ; Second part is done, in 6
    ior 4,6 ; Final result in 4

    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac2,4
    move ac1,ttyout
    move ac3,base2
    nout%
    erjmp done

    hrroi ac1,xorres ; Load 'xor' message
    psout% ; Print to TTY
    setm 4,char1 ; Fetch char1 again
    setm 5,char2 ; Fetch char2 again
    xor 4,5 ; Do xor part of algorithm
    move ac2,4 ; Read to print it
    move ac1,ttyout ; Print to TTY
    move ac3,base2 ; Print as binary
    nout%
    erjmp done ; Handle error (or not)

    done: haltf% ; Exit to monitor
    jrst start ; Restart program?
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Mon Jun 22 10:52:45 2026
    From Newsgroup: alt.lang.asm

    ch6u7.mac

    Demo a jump table. This is the last program in chapter 6 then onto
    chapter 7.

    @exec ch6u7
    MACRO: ch6u7
    LINK: Loading
    [LNKXCT CH6U7 execution]
    Input a 1, 2, or 3 to demo a jump table: 1
    ONE
    @cont
    Input a 1, 2, or 3 to demo a jump table: 2
    TWO
    @cont
    Input a 1, 2, or 3 to demo a jump table: 3
    THREE
    @cont
    Input a 1, 2, or 3 to demo a jump table: 4
    @

    comment $
    Accept a character from the terminal. If the character is a 1, 2, or
    3, then index into the appropriate position in a jump table that
    points to locations that print ONE, TWO, or THREE.
    $
    title ch6u7
    search monsym

    ac1=1
    ac2=2
    ac3=3

    ttyin: .priin ; Input from keyboard
    base10: 12 ; Base 10 in octal
    inmesg: asciz /Input a 1, 2, or 3 to demo a jump table: /
    crlf: 15B6+12B13 ; Bit-pack a \r\n

    start: hrroi ac1,inmesg ; Prompt user for integer
    psout% ; Display message

    subttl input

    ; nin% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Load input device
    move ac3,base10 ; Indicate radix
    nin% ; Get integer
    erjmp done ; Handle error

    caile ac2,3 ; If greater than 3 this is
    jrst done ; an error. Exit.
    caige ac2,1 ; Same with less than 1
    jrst done
    subi ac2,1 ; The jump table is zero-indexed
    jrst tbl(ac2) ; Valid option, use as idx 2 tbl

    subttl jump table

    tbl: jrst tbl.1 ; Jump table via index
    jrst tbl.2
    jrst tbl.3


    tbl.1: hrroi ac1,[asciz /ONE/] ; Table entry for ONE
    psout%
    jumpa tbl.4 ; Skip rest of the output
    tbl.2: hrroi ac1,[asciz /TWO/] ; Table entry for TWO
    psout%
    jumpa tbl.4 ; Skip other text outputs
    tbl.3: hrroi ac1,[asciz /THREE/] ; Table entry for THREE
    psout% ; Fall-thru on this choice
    tbl.4: hrroi ac1,crlf ; Emit newline for proper
    psout% ; output after table message

    done: haltf% ; Exit to monitor
    jrst start ; Restart program?
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Wed Jun 24 22:29:49 2026
    From Newsgroup: alt.lang.asm

    ch7u1.mac
    ch7u2.mac

    Chapter 7 is about pointers. Yup, even in assembler we suffer
    pointers. Also literals, and moving bytes in and out of memory according
    to pointers.

    The first program locates the letter "A" in a string and tells you the position. The second program simulates RDTTY%.

    @exec ch7u1
    LINK: Loading
    [LNKXCT CH7U1 execution]
    Found "A" at position 3

    comment $
    Write a program that will print the numerical positions of the
    letter "A" in a string. Use a 7-bit pointer to gain access to
    each character. You can write the string into the program via ASCIZ.

    This solution starts counting at postion 1, not zero, and accounts
    for the character not being in the string at all.
    $
    title ch7u1
    search monsym

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3
    iter=4 ; Loop iteration counter

    strptr: point 7,[asciz /BCADEF/] ; String, move "A" around to test fndmsg: asciz /Found "A" at position /
    base: 12 ; Base 10 decimal

    start: setz iter, ; Clear counter
    loop: addi iter,1 ; Track position as we move
    ildb ac1,strptr ; Load next byte
    skipn ac1 ; If it's asciz's 0 we're at end
    jrst notfnd ; and thus didn't find it
    caie ac1,"A" ; Is it "A"
    jrst loop ; No, keep checking

    hrroi ac1,fndmsg ; Load found message
    psout% ; Print to TTY
    move ac2,iter ; Set up for NOUT%
    move ac1,[.priou] ; Set primary output w/literal
    move ac3,base ; Decimal base output
    nout% ; Print position
    erjmp [ move 1,[point 7,[asciz /Error./]]
    psout%
    haltf% ] ; Error-handling dance

    notfnd: haltf% ; Exit to monitor, found or not
    lit ; Debugger expand literals
    end start ; Assembler is finished


    The next program was rough, because it deals with walking around a
    pointer to simulate a DEL and a ctrl-U with string input. ctrl-U cancels
    the entire input line on TOPS-20 so that's what my handling of ctrl-U
    ("2" in the program) does in my program.

    @exec ch7u2
    LINK: Loading
    [LNKXCT CH7U2 execution]
    Enter a string, 1=DEL, 2=ctrl-U, Enter=END: ab1cd1e

    The rendered string: ace
    @cont
    Enter a string, 1=DEL, 2=ctrl-U, Enter=END: abcdef1

    The rendered string: abcde
    @cont
    Enter a string, 1=DEL, 2=ctrl-U, Enter=END: abcdef2

    The rendered string:
    @

    comment $
    Write a program that simulates an RDTTY% by looping over a PBIN%. Use
    1 to delete a character, 2 as a ctrl-U, and a carriage return to
    terminate the input loop. Print the message after accepting it.
    Ex:
    AB1CD1E
    ACE
    $
    title ch7u2
    search monsym ; Use monitor's symbols

    ac1=1 ; Label used accum. General input
    ac2=2 ; Generally used for pointers
    ac3=3 ; Generally used for ADJBP
    count=4 ; Track number of entered chars

    size==12 ; Don't show 'size' in debugger
    buffer: block size ; 10 words * 5 = 50 ASCII chars
    ; Each block is 5 chars
    ptrbuf: point 7,buffer ; Pointer to buffer
    etrmsg: asciz /Enter a string, 1=DEL, 2=ctrl-U, Enter=END: /
    renmsg: asciz /
    The rendered string: / ; User prompts and messages

    start: hrroi ac1,etrmsg ; Prompt user for string
    psout% ; Write to TTY

    move ac2,ptrbuf ; Get pointer to buffer for input
    setz ac1, ; Zero out ac1, it will get chars
    setz count, ; No characters input to start
    getchr: pbin% ; Main char input loop, to ac1
    cain ac1,15 ; \r? If so, it signals end
    jrst lf
    cain ac1,"1" ; Is it the delete signal (1)?
    ; Once the user inputs "1", back up the pointer by moving a -1 to an
    ; accumulator then using ADJBP on the accumulator that has the buffer-pointer. ; After, the result is left in ac3 - move this result back to where the
    ; buffer-pointer points, thus updating the contents. Express as a literal.
    jrst [ movni ac3,1
    adjbp ac3,ac2
    movem ac3,ac2
    jumpa getchr] ; Back up pointer in buffer-pointer
    ; using -1, then re-enter getchar loop

    cain ac1,"2" ; Is it the ctrl-u/blank line char?
    jrst [ movn ac3,count
    adjbp ac3,ac2
    movem ac3,ac2
    jumpa getchr] ; Like delete, move pointer back in
    ; buffer-pointer, but this time
    ; according to total chars entered

    idpb ac1,ac2 ; Stuff char to buff, inc pointer
    addi count,1 ; Record that we entered a character
    jrst getchr ; Get next character

    lf: pbin% ; Eat \n, after \r from above
    setz ac1, ; Zero out ac1 to put 0
    idpb ac1,ac2 ; Null terminator for PSOUT%

    hrroi ac1,renmsg ; Load rendered string message
    psout% ; Display it
    move ac1,ptrbuf ; Load pointer to buffer
    psout% ; Print buff contents to TTY
    haltf% ; End program
    jrst start ; Restart program
    lit ; Debugger to show 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Fri Jun 26 21:36:31 2026
    From Newsgroup: alt.lang.asm

    ch7u3.mac
    ch7u4.mac

    The first program emulates MID$ (from BASIC). TOPS-20 has a BASIC so I
    could see how it worked. The second program reads from the terminal and
    stores strings. Then you read from that memory and convert to integers.

    @exec ch7u3
    LINK: Loading
    [LNKXCT CH7U3 execution]
    Enter a short string to demo MID$: Where is Paris?
    Enter an integer position to begin the substring: 10
    Enter an integer reflecting the substring length: 5
    Paris
    @cont
    Enter a short string to demo MID$: Hello world
    Enter an integer position to begin the substring: 0
    Invalid start position

    comment %
    Write a program that simulates a MID$ (in BASIC) function. The program
    should accept a string and two numbers and then print the partial string.
    Ex:
    @basic

    READY
    10 a$ = "Where is Paris?"
    20 print mid$( a$, 10, 5 )
    run

    NONAME.B20
    Thursday, June 25, 2026 11:54:54

    Paris
    %
    title ch7u3
    search monsym

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3
    count=4 ; How many chars in substring?

    size==36 ; 30 dec, don't show sym in debugger buffer: block size ; 30 words * 5 = 150 ASCII chars ptrbuf: point 7,buffer ; Pointer to input buffer
    base10: 12 ; 10 decimal, base we in/output with pos: 0 ; Starting postion of substring
    ttyout: .priou ; Display device
    ttyin: .priin ; Keyboard input device

    strmsg: asciz /Enter a short string to demo MID$: /
    inimsg: asciz /Enter an integer position to begin the substring: /
    endmsg: asciz /Enter an integer reflecting the substring length: /
    outmsg: asciz /
    The substring is / ; User prompts and messages

    subttl input
    start: hrroi ac1,strmsg ; Load user prompt
    psout% ; Display
    move ac1,ptrbuf ; Set up buffer for RDTTY%
    movei ac2,size-1 ; Num chars to read, allow for null end
    setz ac3, ; No ctrl-r, not needed on modern TTY
    rdtty% ; Read the user's test string
    erjmp error ; Handle error

    getn1: hrroi ac1,inimsg ; Load prompt to get start pos
    psout% ; Display it
    ; nin% wants device, radix to 1, 3 and puts results in ac2
    move ac1,ttyin ; Ready NIN%
    move ac3,base10 ; Input base is decimal
    nin% ; Get starting pos number
    erjmp error ; Handle error
    caig ac2,0 ; Don't allow zero or less
    jrst [move ac1,[point 7,[asciz /Invalid start position/]]
    psout%
    jumpa done] ; Exit w/error if not > 1
    subi ac2,1 ; Adjust start position from zero
    ; indexing to one, which is what
    ; a user would expect
    movem ac2,pos ; Save starting position

    getn2: hrroi ac1,endmsg ; Load prompt to get num chars
    psout% ; Display it
    move ac1,ttyin ; Read from TTY
    move ac3,base10 ; Still using decimal
    nin% ; Get num chars to read
    erjmp error ; Handle error
    move count,ac2 ; Save num of chars in substring

    subttl substring
    subs.1: move ac2,ptrbuf ; Load pointer to char buffer
    move ac3,pos ; Fetch starting substring position
    adjbp ac3,ac2 ; Move pointer to new starting pos
    movem ac3,ac2 ; Save result into memory
    subs.2: ildb ac1,ac3 ; Load next byte in ptr ac3 for ac1
    cain ac1,15 ; \r? If so, we're end-of-string
    jrst done ; with nothing more to print, exit
    pbout% ; Print ac1's character
    sojg count,subs.2 ; Loop if still chars (count)

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program

    ; The error message is very generic because this is only an exercise
    error: hrroi ac1,[point 7,[asciz /Error, can't demo MID$/ ] ]
    haltf% ; Exit to monitor
    lit ; Display literals on debug
    end start ; Assembler's job is done


    This program was harder because the text doesn't spend much time
    explaining using NIN% pointed at memory. It gets numbers as strings,
    then ORs them and outputs them as numbers. Think atoi() in C. This
    program also uses a reprompter (ctrl-r), though outside a paper TTY it's
    not that useful.

    @exec ch7u4
    LINK: Loading
    [LNKXCT CH7U4 execution]
    Enter first 5-bit binary number: 00101
    Enter second 5-bit binary number: 01010
    Final result: 01111

    comment $
    Set up an RDTTY%/NIN% to accept two 5-bit binary numbers. IOR the
    numbers; then NOUT% the result in a field of width 5, with leading
    zeroes.
    Additional info on using RDTTY%/NIN% in conjunction: https://www.bourguet.org/v2/pdp10/jsys-user/chap2 , section 2.9
    $
    title ch7u4
    search monsym

    ac1=1 ; Label used accumulators
    ac2=2
    ac3=3
    ac4=4

    num1: block 2 ; 2 * 5 = 10 chars space
    num2: block 2 ; 2 * 5 = 10 chars space
    ptrnm1: point 7,num1 ; Pointer to first buffer
    ptrnm2: point 7,num2 ; Pointer to second buff

    msg1: asciz /Enter first 5-bit binary number: /
    msg2: asciz /Enter second 5-bit binary number: /
    finmsg: asciz /Final result: / ; User messages and prompts

    start: hrroi ac1,msg1 ; Prompt for first number
    psout% ; Display message to TTY
    move ac1,ptrnm1 ; Set up buffer for RDTTY%
    movei ac2,5+2 ; 5 chars plus \r\n
    hrroi ac3,[asciz /First number? /] ; Reprompter message
    rdtty% ; Get first number
    erjmp [move ac1, [point 7, [asciz /Error getting first number /]]
    psout%
    haltf%] ; Handle error here

    hrroi ac1,msg2 ; Prompt for second number
    psout% ; Print to TTY
    move ac1,ptrnm2 ; Set up buffer for RDTTY%
    movei ac2,5+2 ; 5 chars plus \r\n
    hrroi ac3,[asciz /Second number? /]
    rdtty% ; Get second number
    erjmp [move ac1, [point 7, [asciz /Error getting num 2 /]]
    psout%
    haltf%] ; Handle error on num2
    hrroi ac1,finmsg ; Load results message
    psout% ; Print to TTY

    ; This section uses NIN% to read the inputted data from memory and
    ; convert it. In this case, give ac1 a pointer to the buffer.
    ; nin% wants device, radix to 1, 3 and puts results in ac2
    conv: move ac1,ptrnm1 ; Load pointer to buffer, num1
    movei ac3,2 ; Convert to base binary
    nin% ; Fetch number
    erjmp [move ac1, [point 7, [asciz /Conversion error/ ]]
    psout%
    haltf%] ; Handle conv error

    move ac4,ac2 ; Park this for a moment
    move ac1,ptrnm2 ; Load pointer to buff/num1
    movei ac3,2 ; Convert to base binary
    nin% ; Fetch second number
    erjmp [move ac1, [point 7, [asciz /Conversion error/ ]]
    psout%
    haltf%] ; Handle 2nd conv error

    or: ior ac4,ac2 ; Perform the IOR, result in ac4
    ; nout% wants device, number, base in accum 1,2,3 and erjmp
    move ac1,[.priou] ; Indicate TTY as output
    move ac2,ac4 ; The resulting number
    movei ac3,2 ; Indicate binary base output

    ; The left half of ac3 controls the display of the number with NOUT%
    ; Each bit sets a feature. Note "NO%*" type values do not seem to work
    ; even though the text mentions them. Use a binary string instead.
    hrli ac3,^B1110000000000101 ; Leading 0's, 5 places
    nout% ; Print resulting number
    erjmp done ; Handle error (or not)

    done: haltf% ; Exit to monitor
    jrst start ; Restart program
    lit ; Debugger expands 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Tue Jul 7 21:05:17 2026
    From Newsgroup: alt.lang.asm

    ch7u5.mac

    This program uses ildb/idpb to concatenate strings into a buffer and
    then display the result. This is the last exercise for Chapter 7. Onto
    8, which is about stacks and subroutines.

    @exec ch7u5
    LINK: Loading
    [LNKXCT CH7U5 execution]
    Final string: Hello world


    comment $
    Place two strings at two different locations using ASCIZ.
    Move the strings to another area, packing the second string after the
    first string. Use a pointer to display the concatenation.
    $
    title ch7u5
    search monsym, macsym ; Use these two packages

    ac1=1 ; Label accumulators used
    ac2=2
    ac3=3
    count=4 ; Loop iterator

    str1: ascii /Hello / ; Test string one
    str2: asciz /world/ ; Second test string, null term'd buffer: block 3 ; Space for string concatenation
    ; 3 * 5 = 15 chars should be enough ptrbuf: point 7,buffer ; Pointer to above buffer

    start: move ac1,[point 7,str1] ; Get pointer to string one
    move ac2,ptrbuf ; ac2 contains pointer to cat buffer

    ; There's no way I know currently to get a string length when the string is
    ; stored via ASCIZ. For EXP, str1: exp "h","e","l" len=.-str1 works.
    ; Thus, I count manually and load the loop iterator that way.
    movei count,6 ; 6 chars in first string

    mv.1: ildb ac3,ac1 ; Load char from str1 pointer to ac3
    idpb ac3,ac2 ; Move str1 to final buff 1 char @time
    sojg count,mv.1 ; Loop for each character

    move ac1,[point 7,str2] ; Get pointer to string two
    movei count,5+1 ; str2 length plus \0 from ASCIZ
    mv.2: ildb ac3,ac1 ; Load char from str2 pointer to ac3
    idpb ac3,ac2 ; ac2 still has buffer pointer
    sojg count,mv.2 ; Put each character as per count

    TMSG <Final string: > ; From MACSYM.UNV
    move ac1,[point 7,buffer] ; Load pointer to final buffer
    psout% ; Print it

    haltf% ; Exit to monitor
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Tue Jul 14 15:38:20 2026
    From Newsgroup: alt.lang.asm

    ch8u1.mac
    Chapter 8 is long and should probably be split into multiple
    chapters. There's 3 different ways to call subroutines: JSR, JSP, and
    PUSHJ. The JSR one writes the return address into the program code right
    at the label of the procedure. I find that very odd.

    This program calls for JSP, and passing parameters. Then, a test string
    is searched/replaced for target characters (also passed into the
    subroutine). I make use of the "reprompter" function and also STDAC.,
    which comes from the MACSYM.UNV module. There's a terminal output macro
    in there too. Most of the code I see doesn't use STDAC. . The only thing
    I've found out about it is that it likely came from Dan Murphy.

    @exec ch8u1
    LINK: Loading
    [LNKXCT CH8U1 execution]
    Enter a test string to character substitute: hello eels!
    h3llo 33ls!
    @cont
    Enter a test string to character substitute: oh you
    oh you
    @cont
    Test string? nice elements
    nic3 3l3m3nts

    comment $
    Write a subroutine that will substitute a "new" character for an
    "old" character in a given string. Use JSP to call the subroutine,
    and place the three parameters after the call location.

    The parameters:
    pointer to a string
    new character
    old character
    $
    title ch8u1
    search monsym,macsym ; Monitor symbols, MACSYM module
    STDAC. ; Labels accumulators for us

    size==30 ; 24 * 5 = 120 chars space
    newchr=="3" ; Replacement character
    oldchr=="e" ; and old one to search for
    usrstr: block size ; Buffer to hold user's string
    ptrstr: point 7,usrstr ; Pointer to string buffer

    subttl Main
    start: TMSG <Enter a test string to character substitute: >
    move t1,ptrstr ; Set up buffer for RDTTY%
    movei t2,165 ; Space for 117 decimal chars + \r\n\0
    hrroi t3,[asciz /Test string? /] ; Reprompter message on ctrl-r
    rdtty% ; Get user test string
    erjmp [move t1, [point 7, [asciz /Error getting user string/]]
    psout% ; Print a message on read error
    haltf%] ; Exit to monitor, no restart
    jsp p,subst ; Call "substitute" subroutine
    point 7,usrstr ; Pass pointer to string buffer
    newchr ; Pass new replacement character
    oldchr ; Pass in old character to look for

    ; Finally, output the resulting string with any possible modifications
    ; it might have undergone.
    hrroi t1,usrstr ; Point to string buffer
    psout% ; Output to terminal
    haltf% ; Exit to monitor when done
    jrst start ; Repeatable program

    subttl Substitution Subroutine
    ; Given a pointer to a string, search/replace every "old" character
    ; and replace it with the "new" character. This must be a single 1-to-1
    ; character substituion.
    subst: move q1,0(p) ; Load param 0, string buffer pointer
    move q2,1(p) ; Load param 1 to accum, new char
    move q3,2(p) ; Load last param, old char
    ibp q1 ; Initialize to first char in str

    sub.1: ldb t1,q1 ; Get char from string buff via ptr
    cain t1,15 ; Is it \r?
    jrst sub.x ; Yes, we're done with string

    sub.2: camn t1,q3 ; Is it the the old char?
    dpb q2,q1 ; Yes, deposite new replacement char
    ; into the string buff via pointer
    ibp q1 ; Step plus one in string buff ptr
    jumpa sub.1 ; Loop to check full string

    sub.x: jrst 3(p) ; Return, when 3 params passed

    lit ; Expand literals in debugger
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Wed Jul 15 21:15:36 2026
    From Newsgroup: alt.lang.asm

    ch8u2.mac

    This is a FFS program - you have to take a binary number from the user
    and then tell the position of the leftmost set bit. Probably they want
    you to loop using modulus, or maybe bit compare and shift using the
    shift instructions, but I found JFFO. The Longo text doesn't instruct on
    its use but the Gorin text does. It's not cheating, it's being
    smart. ;-)

    Are you surprised to see such an instruction on the PDP-10 KL? Wikipedia
    makes it sound like they're only on more recent machines. They do list the PDP-10 as having it. This allows my subroutine to be basically two instructions.

    @exec ch8u2
    MACRO: ch8u2
    LINK: Loading
    [LNKXCT CH8U2 execution]
    Enter a binary number to check FFS: 00101011
    The left-most set bit (0-35) is 30
    @cont
    Enter a binary number to check FFS: 1
    The left-most set bit (0-35) is 35
    @cont
    Enter a binary number to check FFS: 0
    @cont
    Enter a binary number to check FFS: 101010111
    The left-most set bit (0-35) is 27

    comment $
    Write a subroutine that will specify the location of the left-most
    1 (set bit) in a 36-bit DEC word. Call this routine, and display
    the postion value.
    $
    title ch8u2
    search monsym,macsym ; Use monitor symbols, macsym
    STDAC. ; Label accumulators for us

    usrmsg: asciz /Enter a binary number to check FFS: /
    ttyin: .priin
    ttyout: .priou ; For NIN%/NOUT%

    ; The 36 bit positions that make up a word are numbered, starting on the
    ; left with the 0 bit and ending on the right with bit 35. p42, Longo text
    num: 0 ; User's binary number

    start: hrroi t1,usrmsg ; Load user prompt
    psout% ; Output to TTY
    move t1,ttyin ; Set up NIN%
    movei t3,2 ; Base is binary
    nin% ; Get user's bin number
    erjmp [hrroi t1,[asciz /Input error on NIN%/]
    psout%
    haltf%] ; Exit with message on error
    jumpe t2,done ; Don't compute if num is zero
    movem t2,num ; else save user's number
    jsr findbt ; Go to find bit subroutine

    ; JFFO returns a zero-indexed position, that is, 0-35 with 0 as first
    ; position. We *could* add 1 to get 1-36 but let's not and just note
    ; the bit range to the user.
    TMSG <The left-most set bit (0-35) is >
    move t2,t4 ; Answer was in accum 4
    move t1,ttyout ; Set up NOUT%
    movei t3,12 ; Base is 10 decimal
    nout% ; Print it
    erjmp [hrroi t1,[asciz /IO error on NOUT%/]
    psout%
    haltf%] ; Give message on IO error, exit

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program

    ; Value to act on is in NUM. Return the left-most set bit from NUM's
    ; binary number. Return this in accum 4. This routine never gets a
    ; zero input because of JUMPE before its calling.
    findbt: 0 ; Return address get pasted here!
    setm t3,num ; Fetch user's number to accum 3
    jffo t3,@findbt ; Get first 1 pos to AC+1 (t4)
    ; and exit subroutine

    lit ; Debugger 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Fri Jul 17 22:07:59 2026
    From Newsgroup: alt.lang.asm

    ch8u3.mac

    Difficult program. This is a supposed to be a queue (LIFO) but the text
    only (so far) has taught to a stack (FIFO). If this were C, I'd use a double-linked list. There would be a tail and a head, and if this were
    the end/start, I'd set those to NULL to show the ends of the queue. A
    new node would call malloc(), create a pointer to a structure, and work
    with pointers to those structures in which the node contained the data
    (here, a single decimal integer). But I don't have any of that here.

    Here I use a stack and work the queue within the stack, but the problem
    is the "POP" comes off the bottom, not the top. This means once the
    stack fills I can't add more items. In any case, I think I satisfied the
    quiz requirement even though it's crude.

    @exec ch8u3
    MACRO: ch8u3
    LINK: Loading
    [LNKXCT CH8U3 execution]
    (p)ut or (t)ake an element from the queue (d)=done? p
    Input integer value: 1
    (p)ut or (t)ake an element from the queue (d)=done? p
    Input integer value: 2
    (p)ut or (t)ake an element from the queue (d)=done? p
    Input integer value: 3
    (p)ut or (t)ake an element from the queue (d)=done? t
    Output value is 1
    (p)ut or (t)ake an element from the queue (d)=done? t
    Output value is 2
    (p)ut or (t)ake an element from the queue (d)=done? t
    Output value is 3
    (p)ut or (t)ake an element from the queue (d)=done? t
    Error. Queue is empty (nothing to take).
    (p)ut or (t)ake an element from the queue (d)=done? p
    Input integer value: 4
    (p)ut or (t)ake an element from the queue (d)=done? p
    Input integer value: 5
    (p)ut or (t)ake an element from the queue (d)=done? p
    Input integer value: 6
    (p)ut or (t)ake an element from the queue (d)=done? t
    Output value is 4
    (p)ut or (t)ake an element from the queue (d)=done? t
    Output value is 5
    (p)ut or (t)ake an element from the queue (d)=done? t
    Output value is 6
    (p)ut or (t)ake an element from the queue (d)=done? t
    Error. Queue is empty (nothing to take).
    (p)ut or (t)ake an element from the queue (d)=done? d
    Done.

    comment $
    Write a queue program. Call a PUT subroutine to place a number into
    the queue via a PUSH. Call a TAKE subroutine to remove an element
    from the queue. (Use another accumulator, and treat the stack
    block as an array.)

    Example:
    put or take p
    in value 3
    put or take p
    in value 2
    put or take t
    out value is 3
    put or take d
    done
    $
    title ch8u3
    search monsym,macsym ; Monitor syms, STDAC., etc
    STDAC. ; Label accums for us

    depth==14 ; Stack depth, 12 decimal stack: block depth ; Space for stack itself
    ttyin: .priin ; TTY/keyboard input output ttyout: .priou
    usrcho: "z" ; User's menu choice

    start: reset ; Main program entry
    ; Can't use IOWD stack guard because left-hand side needs to track
    ; total number of items in the stack, later referenced by CX. Set
    ; STACK-1 so first PUSH lands at STACK+0 (STACK). Q1 is zero-indexed.
    move p,[0,,stack-1] ; Set up stack pointer
    setz cx, ; Zero queue item counter
    setz q1, ; Queue item pointer init

    subttl User input
    ; Prompt the user and get one of "p", "t", or "d". Call appropriate
    ; subroutine to do the associated function. "d" exits. Use JSR so that
    ; another stack pointer isn't needed.
    input: TMSG <(p)ut or (t)ake an element from the queue (d)=done? >
    pbin% ; Read user input to ac1
    movem t1,usrcho ; Save user's choice because
    pbin% ; we need to eat \r and \n
    pbin% ; from the input buffer
    move t1,usrcho ; Get back real choice

    ; All these MUST be lowercase, this is only an exercise after all
    cain t1,"p" ; Is it "p"?
    jrst st.1 ; Go do PUT routine
    cain t1,"t" ; Could it be "t"?
    jrst st.2 ; Go do TAKE routine
    cain t1,"d" ; Is it "d"?
    jrst done ; Then we're done
    hrroi t1,[asciz /Invalid input. Please enter p, t, or d
    in lowercase only.
    /] ; Else none of those so
    psout% ; print error msg to TTY
    jumpa start ; Let's try this again

    st.1: jsr put ; PUT routine
    jumpa input ; Loop to get more input
    st.2: jsr take ; TAKE routine
    jumpa input ; Loop to print more queue

    done: TMSG <Done.> ; Annouce completion
    haltf% ; Exit to monitor
    jrst start ; Restartable program


    subttl Put Subroutine
    ; Get a decimal integer from the user and PUSH it onto the stack,
    ; pointed to by stack pointer P. The stack can only grow to DEPTH
    ; amount. The queue must exist in this space.
    put: 0 ; Return address space here
    cail cx,depth ; Don't allow overflow!
    jrst [hrroi t1,[asciz /Error, stack is full!
    /]
    psout%
    jrst @put] ; Exit PUT in this case
    TMSG <Input integer value: > ; Prompt user
    move t1,ttyin ; Get input from keyboard
    movei t3,12 ; Base is decimal
    nin% ; Get number
    erjmp [hrroi t1,[asciz /Error on number input. Exiting./ ]
    psout%
    haltf%] ; Can't cont if no number
    push p,t2 ; Add number to the stack
    ; The left half of the stack pointer has the number of items (PUSHes).
    ; This number can be fetched to another accumulator and examined.
    hlrm p,cx ; Get num of pushes to CX
    jrst @put ; Use this address to return

    subttl Take Subroutine
    ; CX is total number of items in the stack. Items PUSHed onto the stack
    ; are at the top, but we need to access the bottom. Use Q1 as a pointer
    ; to this area to turn a FILO into a LIFO. Unfortunately, because this uses
    ; a stack with fixed depth, adds to the queue can't ever exceed DEPTH -
    ; even if items are "removed" from the queue because nothing is ever removed
    ; from the stack as per the exercises requirements.
    take: 0 ; Reserve space for ret addy
    jumpe cx,t.1 ; Zero items? Don't try TAKE
    caml q1,cx ; Don't exceed total items
    jrst t.1 ; in the queue
    TMSG <Output value is >
    move t1,ttyout ; Set up NOUT% TTY display
    movei t3,12 ; Base 10 decimal
    move t2,stack(q1) ; Get value out of queue
    nout% ; Print it
    erjmp [hrroi t1,[asciz /Error on number output. Exiting.
    /]
    psout%
    haltf%] ; Exit to monitor on error
    addi q1,1 ; Move queue ptr to next item
    hrroi t1,[15B6+12B13] ; New line for readability
    psout% ; Print \r\n
    jrst @take ; Normal subroutine return

    t.1: hrroi t1,[asciz /Error. Queue is empty (nothing to take).
    /]
    psout% ; Output error message
    jrst @take ; Error subroutine return

    lit ; Debugging 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Sun Jul 19 11:07:29 2026
    From Newsgroup: alt.lang.asm

    ch8u4a.mac ch8u4b.mac

    This number addition program demos external routines and stack
    usage. Much easier than that last queue program. Though the text says
    you can use EXEC ch8u4a,ch8u4b to run them I had to COMPILE them both to
    .REL first. Maybe my TOPS-20 version is different, or something changed.

    @exec ch8u4a
    LINK: Loading
    [LNKXCT CH8U4A execution]
    Enter number 1 to add: -5
    Number 2 to add: 18
    The sum of the numbers is 13
    @cont
    Enter number 1 to add: 34
    Number 2 to add: 9
    The sum of the numbers is 43

    -------------------------
    comment $
    Set up an external subroutine library (LIB.MAC) containing a routine that
    will add two numbers. Use a stack to pass the numbers and their sum. Write
    a program (PROG.MAC) that sets up the stack, places the two numbers into the stack, and then calls the external routine. Upon returning, POP and display
    the answer.
    To keep my file naming convension,
    LIB.MAC = ch8u4b.mac
    PROG.MAC = ch8u4a.mac
    $
    title ch8u4a
    search monsym,macsym ; Use monitor's sym, macsym
    STDAC. ; Label accums for us, TMSG
    EXTERN sum ; The addition subroutine
    .REQUIRE ch8u4b ; is in here.

    depth==4 ; 2 parameters + 1 return
    ; address and 1 IOWD stack
    ; watcher equals 4
    stack: block depth ; Space for actual stack

    ttyin: .priin ; For NIN%/NOUT%
    ttyout: .priou

    start: move p,[iowd depth,stack] ; [-depth,,stack-1] Setup stack
    TMSG <Enter number 1 to add: >
    move t1,ttyin ; Set up NIN%
    movei t3,12 ; Base is decimal
    nin% ; Get first number
    erjmp [hrroi t1,[asciz /Error on NIN%, exiting./]
    psout%
    haltf%] ; Exit on input error

    move p1,t2 ; Save 1st parameter
    TMSG <Number 2 to add: > ; Prompt user
    move t1,ttyin ; This is probably already here
    movei t3,12 ; Likewise, but be safe
    nin% ; Get number 2
    erjmp [hrroi t1,[asciz /Error on NIN%, exiting./]
    psout%
    haltf%] ; Exit on input error

    move p2,t2 ; Save 2nd parameter

    push p,p1 ; PUSH first num onto stack
    push p,p2 ; Likewise for second
    pushj p,sum ; Go to subroutine to add

    TMSG <The sum of the numbers is > ; Label output
    move t1,ttyout ; Setup NOUT%
    movei t3,12 ; Base is decimal
    pop p,t2 ; Get sum parameter off stack
    nout% ; and print it.
    erjmp [hrroi t1,[asciz /Error on NOUT%, exiting./]
    psout%
    haltf%] ; Exit with message on error

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    lit ; Debugger to expand literals
    end start ; Assembler is done

    -------------------------------------
    comment $
    Module for use with ch8u4a.mac. This module must be compiled ahead of time
    to be used with ch8u4a. This subroutine takes two numbers off the stack,
    adds them, and pushes the result back onto the stack for the caller. The
    stack pointer is "p" as per STDAC. .
    Params: int, int
    $
    title ch8u4b
    search monsym,macsym ; Use monitor syms, macsym
    STDAC. ; Label accums for us
    entry sum ; Usable subroutine

    sum: pop p,q1 ; Save return addy for later
    pop p,p1 ; Get number 1
    pop p,p2 ; Get number 2
    add p1,p2 ; Add the two numbers and
    push p,p1 ; push the result onto stack
    push p,q1 ; Put back return address
    popj p, ; Return to PUSHJ caller
    end ; Assembler takes a break
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Wed Jul 22 10:56:08 2026
    From Newsgroup: alt.lang.asm

    ch8u5.mac

    Pass pointers to strings on the stack back into the main program from a subroutine. The directions aren't very specific on when this needs to
    happen, but it sounds like to me that the strings are to be acquired in
    the subroutine and the stack loaded with pointers then. I use JSR so
    that I don't have to setup another stack or deal with the subroutine's
    address on the stack that I'm using for pointers (though I could if I
    needed to). Use the right tool in the tool box, correct?


    @exec ch8u5
    LINK: Loading
    [LNKXCT CH8U5 execution]
    Enter some strings to test a routine. Max 120 chars per string.
    Enter string 1: hello
    Enter string 2: all you
    Enter last string: eels there

    First string: hello
    Second string: all you
    Third string: eels there


    comment $
    Write a routine that will accept three strings from the terminal. PUSH
    the pointers of each string onto a stack. When the last string has been
    typed in, POP each pointer and display the strings.
    $
    title ch8u5
    search monsym,macsym ; Use monitor's sym, macsym
    STDAC. ; Label accums for us

    size==30 ; Size of string, 24*5=120 chr
    depth==4 ; Stack depth: 3 items + watch ustr1: block size ; User string one
    ustr2: block size ; 2
    ustr3: block size ; 3
    stack: block depth ; Stack space
    errmsg: asciz /IO error on RDTTY%/ ; General error message

    subttl Main
    start: move p,[iowd depth,stack] ;[-depth,stack-1] Setup stack
    TMSG <Enter some strings to test a routine. Max 120 chars per string.

    jsr getstr ; Use JSR; only one stack
    pop p,p3 ; Get 3rd string off stack
    pop p,p2 ; Likewise with 2nd
    pop p,p1 ; First string
    TMSG <
    First string: >
    move t1,p1 ; Already a pointer, no HRROI
    psout% ; Print it
    TMSG <Second string: >
    move t1,p2 ; Pointer to second
    psout% ; Print it
    TMSG <Third string: >
    move t1,p3 ; Pointer to last
    psout% ; Print it
    done: haltf% ; Exit to monitor
    jrst start ; Restartable program


    subttl Get String Subroutine
    ; Input: none
    ; Output: The pointers to the 3 user strings retrieved by this
    ; routine are placed on the stack, p: 1st, 2nd, 3rd strings
    getstr: 0 ; Save room for return addr
    TMSG <Enter string 1: >
    move t1,[point 7,ustr1] ; Pointer to 1st string
    push p,t1 ; Save pointer on stack
    movei t2,165 ; Space for 117 chrs +\r\n\0
    hrroi t3,[asciz /String 1? /] ; Reprompter ctrl-r mesg
    rdtty% ; Get user string
    erjmp [hrroi t1, errmsg
    psout%
    haltf%] ; Exit with mesg on error
    TMSG <Enter string 2: >
    move t1,[point 7,ustr2] ; Pointer to 2nd string
    push p,t1 ; Save ustr2 pointer on stack
    movei t2,165 ; Char count for RDTTY%
    hrroi t3,[asciz /String 2? /] ; Reprompter
    rdtty% ; Get user input
    erjmp [hrroi t1, errmsg
    psout%
    haltf%] ; Exit with mesg on error
    TMSG <Enter last string: >
    move t1,[point 7,ustr3] ; Get last pointer
    push p,t1 ; And save it
    movei t2,165 ; Char count
    hrroi t3,[asciz /String 3? /] ; ctrl-r reprompter
    rdtty% ; Get user input
    erjmp [hrroi t1, errmsg
    psout%
    haltf%] ; Exit with mesg on error
    jrst @getstr ; All ptrs on stack, return

    lit ; Expand literals in DDT
    end start
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Sun Jul 26 16:18:43 2026
    From Newsgroup: alt.lang.asm

    This program writes into itself by using a pointer, so that it knows
    what stack to use. The directions for sorting aren't very clear. This is
    a strange program and was no fun to write although XCT was an
    interesting instruction to learn about.


    @exec ch8u6
    LINK: Loading
    [LNKXCT CH8U6 execution]
    All numbers sorted. Showing stack contents:
    0
    1
    2
    3
    4
    5
    6
    7
    @

    comment $
    Sort a group of octal numbers using eight stacks as described in the
    text (Chapter 8, exercise demo 23).

    Programmer's notes: Exercise demo 23 sorts numbers into even/odd,
    using two stacks. Since there's only two cases (even, odd) eight
    stacks doesn't make sense for even/odd sorting, and the text
    doesn't make clear "how" to sort or on what basis. I assume they
    want one number per stack. That is, (zero-indexed) number zero
    goes in stack zero, one in stack one, and so on.
    $
    title ch8u6
    search monsym

    depth==1 ; Size of each stack
    count=16 ; ac for indexer/counter
    ; FAIL assembler will not use EXP. Space after EXP (not tab). List
    ; one number per line and remove EXP if using FAIL.
    data: exp 0, 1, 2, 3, 4, 5, 6, 7 ; Dataset to sort

    stack0: block depth ; Space for stacks
    stack1: block depth ; 8 per text request
    stack2: block depth
    stack3: block depth
    stack4: block depth
    stack5: block depth
    stack6: block depth
    stack7: block depth

    ; This pointer is 3 bits wide and points into HERE, at bit 12, which
    ; is the bit that determines which stack PUSH uses. Later HERE gets XCT'd.
    ; Largest stack number is 7, which is ^B111, which needs 3 bits to store. ptrstk: point 3, here, 12 ; Points to stack changer

    ; Note that the stack accum is determined by the above pointer editing HERE
    ; as the program executes. accum 10 is containing the value (number) to push here: push ,10 ; Will be PUSH N,10 later

    subttl Main
    start: move 0,[0,,stack0-1] ; Set up stacks
    move 1,[0,,stack1-1]
    move 2,[0,,stack2-1]
    move 3,[0,,stack3-1]
    move 4,[0,,stack4-1]
    move 5,[0,,stack5-1]
    move 6,[0,,stack6-1]
    move 7,[0,,stack7-1]
    setz count, ; Zero counter/indexer

    subttl Get Numbers
    loop: move 10,data(count) ; Get next data item from set
    dpb 10,ptrstk ; Place bits into bit 12 HERE
    xct here ; and exec composed instruction
    addi count,1 ; Inc loop/indexer
    caie count,10 ; Do all 8 numbers
    jrst loop ; Go back through loop
    hrroi 1,[asciz /All numbers sorted. Showing stack contents:
    /]
    psout%

    subttl Output Numbers
    setz count, ; Clear counter/indexer
    movei 3,12 ; NOUT% base 10 output
    show: move 1,[.priou] ; Output to TTY
    move 2,stack0(count) ; Top of stacks, note block
    nout% ; depth is 1 for nice indexing
    erjmp [move 1,[asciz /Error on NOUT%. Exiting./]
    psout%
    haltf%] ; Mesg + halt on error
    hrroi 1,[15B6+12B13] ; Bit pack \r\n
    psout% ; Print it
    addi count,1 ; Increment counter
    caie count,10 ; Do all 8 numbers
    jrst show ; by looping
    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    lit ; Debugger 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Tue Jul 28 20:12:29 2026
    From Newsgroup: alt.lang.asm

    ch8u7a.mac ch8u7b.mac

    Old tools are great, they allow you to have much more "fun" than you'd
    normally be able to have. For example, the assembler. It told me there
    was an undefined symbol "FACTOR", which is the subroutine in this
    program. I had 3 hours and 22 minutes of "fun" looking at why this
    was. Come to find out it was an unterminated comment block. Would MACRO
    tell me this? Of course not; that would be too easy, and thus no "fun".

    This program uses a recursive subroutine to calculate N!, though it's
    limited to 13! due to accumulator bit size. MUL could be used in place
    of IMUL, but that would require a rewrite and reworking of the
    subroutine which is more effort than I wish to spend on this
    program. As-is the assignment is satisfied and I continue on to Chapter
    9.

    @exec ch8u7b,ch8u7a
    LINK: Loading
    [LNKXCT CH8U7A execution]
    Enter an integer to compute N! (max int = 13): 3

    The number factorial is 6
    @cont
    Enter an integer to compute N! (max int = 13): 13

    The number factorial is 6227020800
    @

    comment $
    Write a recursive factorial routine that accepts numbers using RDTTY%/
    NIN% and checks for negative numbers (see examples Chapter 8: 25, 26).

    "factor" subroutine is in ch8u7b.mac. If not using EXTERN, you can use
    .REQUIRE ch8u7b after that file has been assembled to a .REL file
    $
    title ch8u7a
    search monsym, macsym
    STDAC. ; Label accumulators
    extern factor ; .exec ch8u7b,ch8u7a

    ; The largest N value this program allows is 13. 13 return address spaces
    ; plus 13 nums, plus one space for stack guard.
    maxnum==15
    depth==maxnum * 2 + 1 ; Stack depth
    usrnum: 0 ; RDTTY% buffer for user num stack: block depth ; Space for stack

    subttl Main
    start: move p, [iowd depth, stack] ; Setup stack w/OF guard
    TMSG <Enter an integer to compute N! (max int = 13): >
    move t1, [point 7, usrnum] ; Ptr to buff for RDTTY%
    movei t2, 2+2+1 ; Read 2 chars for NN and
    ; possibly a "-" + \r\n
    hrroi t3, [asciz /Integer? /] ; Reprompter message
    rdtty% ; Get user num to buffer
    erjmp [hrroi t1, [asciz /Error on RDTTY%. Exiting./]
    psout%
    haltf%] ; Exit with error if no num

    ; Use NIN% on RDTTY%'s buffer to get integer to acc 2
    move t1, [point 7, usrnum] ; Pointer to RDTTY% buffer
    movei t3, 12 ; Decimal base input
    nin% ; Get user's number to ac 2
    erjmp [hrroi t1, [asciz /Error on NIN%. Exiting./]
    psout%
    haltf%] ; Exit with error if no num

    ; The user's number should now be in accum 2. Check if in range before
    ; calling the subroutine to do N! . 13! is the largest this program can
    ; handle because of the algorithm and IMUL usage. Any larger and the
    ; result won't fit into one accumulator. This would require use of MUL
    ; and a re-write of FACTOR.
    caile t2, maxnum ; Is user's num <= maxnum?
    jrst [hrroi t1, [asciz /Number too large. Try smaller number.
    /]
    psout%
    jrst done] ; Exit if not
    caige t2, 0 ; Is user's num >= zero?
    jrst [hrroi t1, [asciz /Number must be greater than or equal zero. /]
    psout%
    jrst done] ; Exit if not
    pushj p, factor ; Valid input now in acc 2

    subttl Output
    output: TMSG <
    The number factorial is > ; Print label for output
    move t1, [.priou] ; Set up NOUT%
    movei t3, 12 ; Decimal base
    nout% ; Print number
    erjmp done ; Handle error. Or not.

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    lit ; Debugger to expand literals
    end start ; Assembler is done


    comment $
    Subroutine to find N!. Assumes stack 'p' already setup. Result must fit
    into one accumulator due to IMUL and algorithm/stack.
    Calling: called with PUSHJ
    Input: integer in accumulator 2, 0 <= N <= 13
    Output: Returns result in same
    $
    title ch8u7b
    search monsym, macsym
    STDAC. ; Label accums for us
    entry factor ; Subroutine to find N!

    factor: skipn t2 ; Handle zero. 0! is 1
    jrst [movei t2, 1
    jrst f.1] ; Set to 1 and bail
    caig t2, 1 ; If N=1 then return
    popj p,
    push p, t2 ; Save current acc 2 value
    subi t2, 1 ; Construct N-1
    pushj p, factor ; Now call with N-1
    pop p, q1 ; Get last acc 2 value
    imul t2, q1 ; Form N * FACTOR(N-1)
    f.1: popj p, ; Return to caller

    end
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Sat Aug 1 15:38:02 2026
    From Newsgroup: alt.lang.asm

    ch9u1.mac

    Chapter 9 deals with macros, "universal" files, and a few other
    things. The instructions aren't clear if I'm supposed to nest the SHOW
    in the IF macro or the IF in the SHOW or place them separate. Here, they
    are separate. See the comment section for a description of the program.

    @exe ch9u1
    LINK: Loading
    [LNKXCT CH9U1 execution]
    Enter a test number: 2
    Enter another number: 7
    The sum of the numbers is 9, PLUS
    @cont
    Enter a test number: -3
    Enter another number: 1
    The sum of the numbers is -2, NEGATIVE
    @cont
    Enter a test number: -5
    Enter another number: 5
    The sum of the numbers is 0, ZERO

    comment $
    Using macros, design a "language" with the following tokens: PRINT,
    SHOW, GET, and IF. The syntax of the language is:

    PRINT mess mess is an ASCII string
    SHOW var var is a numerical variable
    GET var accepts a number the TTY and places in var
    ADD ans, var1, var2 adds nums var1, var2, places sum in ans
    IF var, less, eq, great an arithmetic jump
    jump to less if var < 0
    jump to eq if var = 0
    jump to great if var > 0

    Write a program that will accept (GET) two numbers, add them, and print
    (SHOW) the phrases NEG, ZERO, or PLUS based on the value of the sum
    of the numbers.

    Notes: "ADD" can't be used as the macro name because if "add" (opcode)
    is used in the macro (it is), the assembler gets confused. If the name
    of the macro is ADD, then the macro definition includes itself if the
    word "add" is used. Thus, "ADD" is called "MYADD" in my program.
    $
    title ch9u1
    search monsym,macsym ; Make use of .UNV symbols
    lall ; Expand MACROS
    STDAC. ; Label accums

    ttyin: .priin ; For use with NIN/NOUT% ttyout: .priou
    usrn1: 0 ; Space to store user's nums usrn2: 0 ;
    result: 0 ; Used by MYADD

    subttl Macro Definitions
    ; Print a text message to the screen. () for parameters, <> for a
    ; single parameter. No space in macro parmas, even after commas,
    ; else bad things can happen, especially if using concatenation.
    define PRINT (mess) <
    hrroi t1, [asciz /mess/]
    psout%


    ; Display a number that was stoared in memory such as NOUT% works
    define SHOW (var) <
    move t1,ttyout
    move t2,var
    movei t3,12 ; Base 10 decimal
    nout%
    erjmp [hrroi t1, [asciz /Error on NOUT%/]
    psout%
    haltf%] ; Halt on error with message


    ; Like NIN%, but then saves number to memory location
    define GET (var) <
    move t1,ttyin ; Get from TTY
    movei t3,12 ; Base 10 decimal
    nin%
    erjmp [hrroi t1, [asciz /Error on NIN%/]
    psout%
    haltf%] ; Halt on error with message
    movem t2, var


    ; Adds param 2, 3 and places results in 1. The number are stored in
    ; memory.
    define MYADD (ans,var1,var2) <
    move t1, var1
    move t2, var2
    add t1, t2
    movem t1, ans ; Save result to memory


    ; Branches depending on if var is greater, less, or equal to zero.
    ; var must be moved in from memory.
    define IF (var,%less,%eq,%great,%done) <
    move t1, var ; Load var to accum
    jumpe t1, %eq ; Is it zero?
    jumpl t1, %less ; Less than zero?
    jumpg t1, %great ; Greater than?
    %less: hrroi t1,[asciz /, NEGATIVE
    /]
    psout% ; Print tag
    jrst %done ; Finished
    %eq: hrroi t1,[asciz /, ZERO
    /]
    psout% ; Print tag
    jrst %done ; Finished
    %great: hrroi t1,[asciz /, PLUS
    /]
    psout% ; Print tag
    %done: >

    subttl Main
    start: PRINT (Enter a test number: )
    GET (usrn1)
    PRINT (Enter another number: )
    GET (usrn2)
    MYADD (result,usrn1,usrn2)
    PRINT (The sum of the numbers is )
    SHOW (result)
    IF (result)

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    lit ; Debugger 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Mon Aug 3 17:48:40 2026
    From Newsgroup: alt.lang.asm

    ch9u2.mac ch9u3.mac
    These are two macros and their testing programs. One works like BASIC's
    MID$, and the other is a LENgth of the string finder.

    This MID$ is zero-indexed. The one in a previous example was
    one-indexed.

    @exec ch9u2
    LINK: Loading
    [LNKXCT CH9U2 execution]
    Enter a short string to demo MID$: The moon is blue
    Enter an integer position to begin the substring: 4
    Enter an integer reflecting the substring length: 3
    moo
    @cont
    Enter a short string to demo MID$: Hello all you eels.
    Enter an integer position to begin the substring: 14
    Enter an integer reflecting the substring length: 4
    eels


    comment %
    Write a macro that simulates the MID$ fuction in BASIC. Pass to the macro
    MID$ a pointer and two numbers. The pointer points to a string, the
    first number is the beginning position of the partial string, and the
    second number is the length of the partial string.

    This program is very similar to ch7u3 but uses a macro instead. Unlike
    that one, this macro is zero-index (ch7u3 was one-indexed).
    %
    title ch9u2
    search monsym, macsym ; Get .UNV symbols
    lall ; Expand macro listings
    STDAC. ; Label accums

    size==36 ; Size of block for demo str buffer: block size ; 30 words * 5 = 150 chars ptrbuf: point 7, buffer ; Pointer to input buffer

    strmsg: asciz /Enter a short string to demo MID$: / inimsg: asciz /Enter an integer position to begin the substring: /
    endmsg: asciz /Enter an integer reflecting the substring length: /
    outmsg: asciz /
    The substring is / ; User prompts and messages

    ; Simulate MID$ function in BASIC. Takes a pointer to string, begin pos,
    ; length, in memory locations or accumulators. Greater length than string
    ; outputs the whole string (looks for \r\n).
    define MID (ptr,begin,length,%loop,%fin) <
    move t2, ptr ; Load pointer to char buff
    move t3, begin ; Fetch starting substr pos
    move cx, length ; Load length (counter)
    adjbp t3, t2 ; Move ptr to new start pos %loop: ildb t1, t3 ; Load next byte to T1 from T3
    cain t1, 15 ; \r? If so, at end-of-string
    jrst %fin ; with nothing more to print
    pbout% ; Print T1's char to TTY
    sojg cx, %loop ; Loop if more chars as per CX %fin: >


    subttl Main
    start: hrroi t1, strmsg ; Load user prompt
    psout%
    move t1, ptrbuf ; Set up RDTTY%
    movei t2, size-1 ; Max chars to read + end
    hrroi t3, [asciz /Demo string? /] ; Reprompter
    rdtty% ; Read user's demo string
    erjmp [hrroi t1, [asciz /Error on RDTTY%. Exiting./]
    psout%
    haltf%] ; Exit if no string
    getn1: hrroi t1, inimsg ; Load get pos prompt
    psout%
    move t1, [.priin] ; Read NIN% for TTY
    movei t3, 12 ; Base 10 decimal
    nin%
    erjmp [hrroi t1, [asciz /Error on NIN%. Exiting./]
    psout%
    haltf%] ; Exit if no start pos
    caige t2, 0 ; Don't allow negative #
    jrst [hrroi t1, [asciz /Invalid start position./]
    psout%
    haltf%] ; Exit if negative else...
    move q1, t2 ; Save start pos for later

    getn2: hrroi t1, endmsg ; Load num chars prompt
    psout% ; Print to TTY
    move t1, [.priin] ; Ready NIN% again
    movei t3, 12 ; Base 10 decimal
    nin% ; Get num chars to read
    erjmp [hrroi t1, [asciz /Error on NIN% num chars. Exiting./]
    psout%
    haltf%] ; Exit if can't get
    caig t2, 0 ; Length must be pos > 0
    jrst [hrroi t1, [asciz /Length must be greater than zero. Exiting./]
    psout%
    haltf%] ; Exit if bad substring length
    move q2, t2 ; Else save num chars for later

    ; Error-checking happens before macro is called, not in macro
    MID (ptrbuf,q1,q2) ; Print Q2 chars starting at
    ; Q1 from str ptr ptrbuf

    done: haltf% ; Exit to monitor
    jrst start ; Restart program
    lit ; Expand literals
    end start ; Assembler is done


    This is LEN(). To demo the case where the string doesn't end in \r\n,
    you have to use the hardcoded ptrnul pointer. As-is it demos a
    user-input string that will end in \r\n from the keyboard.

    @exec ch9u3
    LINK: Loading
    [LNKXCT CH9U3 execution]
    Enter a short string to demo string length finder: hello world
    The string length is 11
    @cont
    Enter a short string to demo string length finder: eels
    The string length is 4
    @


    comment $
    Write a macro that will accept a string pointer and return the
    number of characters in the string being pointed to.

    This macro does not count the \r\n, but does account for if
    the string does not contain \r\n but rather ends in NULL.
    $
    title ch9u3
    search monsym, macsym
    lall ; Expand macros
    STDAC. ; Label accums

    size==36 ; Max size of test string buffer: block size ; Buffer for test string ptrbuf: point 7, buffer ; Pointer to above
    strmsg: asciz /Enter a short string to demo string length finder: /

    ; For testing a string that doesn't end with \r\n but rather a NULL
    strnul: asciz /Testing/ ; No \r\n
    ptrnul: point 7, strnul ; Pointer to above

    ; Return length of string pointed to by "ptr", place results in Q1, uses
    ; CX as counter. If used in a real program, this should save CX and
    ; probably return the results on a stack.
    define LEN (ptr,%loop,%fin) <
    move t2, ptr ; Load pointer to char buff
    setz cx, ; Zero counter
    setz t3, ; Start at pos 0 in string
    adjbp t3, t2 ; Move ptr to start position %loop: ildb t1, t3 ; Get first character to T1
    cain t1, .CHCRT ; Is \r? (in MACSYM)
    jrst %fin ; Yes, done counting
    cain t1, .CHNUL ; Is it NULL (for ASCIZ)?
    jrst %fin ; Yes, done counting
    addi cx, 1 ; None of those, inc count
    jumpa %loop ; JUMPA is unloved :(
    %fin: move q1, cx ; Save char number to Q1


    subttl Main
    start: hrroi t1, strmsg ; Load user prompt
    psout% ; Output to TTY
    move t1, ptrbuf ; Set up RDTTY% pointer
    movei t2, size-1 ; Max chars to read
    hrroi t3, [asciz /Demo string? /] ; Reprompter ctrl-r string
    rdtty%
    erjmp [hrroi t1, [asciz /Error on RDTTY%. Exiting./]
    psout%
    haltf%] ; Exit if no string

    subttl Output
    outnum: hrroi t1, [asciz /The string length is /]
    psout%
    LEN (ptrbuf) ; Get str length to Q1
    move t1, [.priou] ; Ready NOUT%
    move t2, q1 ; The number to print
    movei t3, 12 ; Decimal number base
    nout%
    erjmp done ; Handle error. Or not.

    done: haltf% ; Exit to monitor
    jrst start ; Restart program?
    lit ; 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Wed Aug 5 17:08:32 2026
    From Newsgroup: alt.lang.asm

    ch9u4a.mac, ch9u4b.mac
    ch9u5.mac

    Some macro programs again. The first one uses a .UNV file (don't forget
    to recompile your files after changing anything to do with them), the
    second searches for a character in a string and returns the position of
    the first occurance. This macro is zero-indexed.

    @exec ch9u4a
    LINK: Loading
    [LNKXCT CH9U4A execution]
    Enter number to square: 4
    Number squared is 16
    @cont
    Enter number to square: -3
    Number squared is 9
    @

    comment $
    Write a macro SQUARE( in, out ). This macro places the square of the
    parameter "in" into the parameter "out". Place this macro into a
    universal file. Write a program that invokes this macro.

    ch9u4a - driver code
    ch9u4b - macro code universal file
    $
    title ch9u4a
    search monsym, macsym, ch9u4b ; Needed .UNVs
    lall ; Expand macros
    STDAC. ; Label accumulators

    etrnum: asciz /Enter number to square: / ; User prompts and messages outnum: asciz /Number squared is /
    result: 0 ; Result of sqr( num )

    subttl Main
    start: hrroi t1, etrnum ; Load user prompt
    psout% ; Print to tty
    move t1, [.priin] ; Ready NIN%
    movei t3, 12 ; Decimal base
    nin% ; Read from keyboard
    erjmp [hrroi t1, [asciz /Error on NIN%. Exiting./]
    psout%
    haltf%] ; Exit if no number input

    hrroi t1, outnum ; Load output prompt
    psout%
    SQUARE (t2,result) ; Use sqr macro
    move t2, result ; Load result
    move t1, [.priou] ; Ready NOUT%
    movei t3, 12 ; Decimal base
    nout% ; Print to TTY
    erjmp [hrroi t1, [asciz /Error on NOUT%. Exiting./]
    psout%
    haltf%] ; Exit if can't output
    done: haltf% ; Exit to monitor but
    jrst start ; allow restart on success
    lit ; Debugger to expand literals
    end start ; Assembler is done


    comment $
    Macro definition for exercise ch9u4a, SQUARE(in,out) as a .UNV file. Must
    be assembled before use and again after any changes are made to this file.

    Input: Number to square in memory location 'in'
    Output: Returns result of 'in'^2 in memory location 'out'.
    $
    universal ch9u4b ; Identify this as .UNV file

    define SQUARE (in,out) <
    move t1, in ; Load passed-in number
    imul t1, t1 ; Multiply num by itself
    movem t1, out ; Save it to 'out' mem loc


    end ; Assembler is done


    Note this one is zero-indexed, so position 4 would be as 5 as a person
    counts.

    @exec ch9u5
    LINK: Loading
    [LNKXCT CH9U5 execution]
    Enter a short test string: Hello all you eels.
    Enter a char to search your string for: o
    4
    @cont
    Enter a short test string: Oooh hell-o.
    Enter a char to search your string for: x
    0

    comment $
    Write a macro that accepts a string pointer and a character. The macro
    will return the numerical position where the character occurs in the
    string. If the character is not in the string, return a 0.
    $
    title ch9u5
    search monsym, macsym
    lall ; Let's see the macros
    STDAC. ; Label accums and such

    size==36 ; Max size of test string usrstr: asciz /Enter a short test string: / ; User prompts
    getchr: asciz /Enter a char to search your string for: /
    usrchr: "z" ; User's test char
    buffer: block size ; Store user's string
    ptrbuf: point 7, buffer ; Pointer to above buffer

    ; Find (1st) position of chr in string pointer ptr (zero-indexed)
    define CHRFND (ptr,chr,%loop,%fin) <
    move t1, chr ; Load findable char from mem
    move t2, ptr ; Load pointer to work on
    setz cx, ; Zero count
    setz t4, ; Init pos-found-at accum %loop: ildb t3, t2 ; Get 1st char from T2 to T3
    cain t3, .CHCRT ; Is \r?
    jrst %fin ; Reached end of \r\n string
    cain t3, .CHNUL ; Is it NULL (for ASCIZ)?
    jrst %fin ; Reached end of ASCIZ string
    camn t3, t1 ; Is it findable chr?
    jrst [move t4, cx ; Yes, note 1st position
    jrst %fin] ; and leave loop, done
    addi cx, 1 ; Increment counter
    jumpa %loop ; Loop until end-of-string
    ; T4 now either contains the postition, or it stayed at zero (not found).
    ; Either way we have a number to print to TTY. Do that now.
    %fin: move t1, [.priou] ; Ready NOUT%
    move t2, t4 ; Pos or zero (not found)
    movei t3, 12 ; Decimal base
    nout%
    erjmp [hrroi t1, [asciz /Error on NOUT%. Exiting./]
    psout%
    haltf%] ; Exit w/message on error



    subttl Main
    start: hrroi t1, usrstr ; Load user prompt
    psout% ; Print
    move t1, ptrbuf ; Ready RDTTY%
    movei t2, size-1 ; Max chars to read
    hrroi t3, [asciz /Test string? /] ; Reprompter message
    rdtty%
    erjmp [hrroi t1, [asciz /Error on RDTTY%. Exiting./]
    psout%
    haltf%] ; Exit if no string
    hrroi t1, getchr ; Lad get char prompt
    psout%
    pbin% ; Get char to T1
    movem t1, usrchr ; and save it for later
    pbin%
    pbin% ; Eat \r\n. Crude, but works

    m1: CHRFND (ptrbuf,usrchr) ; Print location to TTY

    done: haltf%
    jrst start ; Restartable
    lit ; Expand literals
    end start ; Assembler is done


    Now on to chapter 10, which is about files.
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Sat Aug 8 14:03:16 2026
    From Newsgroup: alt.lang.asm

    chau1.mac

    Chapter 10 deals with files. I've taken to numbering the chapters in hexadecimal so as to keep the file names reflecting the chapter/exercise
    at less than or equal to 6 characters + extension because TOPS-20 does
    not like [1] longer file names.

    @type three.dat
    765

    @exec chau1
    MACRO: chau1
    LINK: Loading
    [LNKXCT CHAU1 execution]
    Reading three.dat file: 765

    comment $
    Using the system editor, create a file and place three numbers in it.
    Write a program that will access this file and display its contents.

    three.dat: example data file for this exercise
    $
    title chau1 ; Chapter 10, user exercise 1
    search monsym, macsym ; Search .UNV symbols
    STDAC. ; Lable accums in std fasion

    amt==3 ; Number of chars to read

    start: reset ; Working with files, RESET
    hrroi t1, [asciz /Reading three.dat file: /]
    psout% ; Output heading

    ; First get Job File Number. Set JFN flags in ac1, and file in ac2 or
    ; point to terminal if the user is to enter the file name.
    move t1, [GJ%SHT+GJ%OLD] ; Set file open flags
    move t2, [point 7, [asciz /three.dat/]] ; Point to file to read
    gtjfn% ; Get JFN
    erjmp [hrroi t1, [asciz /Error on getting JFN. Exiting./]
    psout%
    haltf%] ; Exit with mesg on JFN error
    move q1, t1 ; Save JFN for later in Q1

    move t2, [OF%RD+7B5] ; Setup file open for read
    openf% ; JFN still in T1, open
    erjmp [hrroi t1, [asciz /Error on file open. Exiting./]
    psout%
    haltf%] ; Exit with mesg on JFN error
    movei cx, amt ; Load char amount for looping inloop: move t1, q1 ; Load JFN
    bin% ; Read char from open file
    move t1, t2 ; Shuffle char to T1
    pbout% ; Display character
    sojg cx, inloop ; Repeat input loop as per CX

    move t1, q1 ; Reload JFN for close
    closf% ; Close
    erjmp [hrroi t1, [asciz /Error on file close./]
    psout%
    haltf%] ; Exiting, but no restart

    done: haltf% ; Exit to monitor
    jrst start ; Restartable on succcess
    lit ; Expand literals
    end start ; Assembler is done


    [1] Actually, the OS will take longer file names, but many of the tools
    will complain and not operate. Thus, it's best to stay at 6 characters
    per name. "Gee, MS-DOS - your mother lets you have 8 chars in a file
    name?!"
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Mon Aug 10 18:19:47 2026
    From Newsgroup: alt.lang.asm

    chau2.mac readb.mac
    This exercise writes numbers to files, but not in character mode. Basically
    move t2, [OF%APP+44B5] versus
    move t2, [OF%APP+7B5]

    This means you can't check them with TYPE to see if the exercise was successful. If you try, it just prints junk to the terminal The solution
    I found was to make another utility to read them back and print out the
    results to the terminal. This utility I named "readb", in readb.mac .

    @exec chau2
    MACRO: chau2
    LINK: Loading
    [LNKXCT CHAU2 execution]
    Enter three numbers, each followed by \r\n.
    12
    7
    6
    Enter file name to write to: three.dat
    @type three.dat
    ^F^C^C <-- not readable
    @exec readb
    LINK: Loading
    [LNKXCT READB execution]
    *** [READ B]inary file utility ***
    Enter the file name to check: three.dat
    12
    7
    6
    @exec chau2
    LINK: Loading
    [LNKXCT CHAU2 execution]
    Enter three numbers, each followed by \r\n.
    -20
    0
    4
    Enter file name to write to: three.dat
    @exec readb
    LINK: Loading
    [LNKXCT READB execution]
    *** [READ B]inary file utility ***
    Enter the file name to check: three.dat
    12
    7
    6
    -20
    0
    4

    comment $
    Write a program that will append three numbers to an existing file.
    Write the program to supply the name of the file and the three numbers
    at run time.
    $
    title chau2 ; Chapter 10 user exer 2
    search monsym, macsym
    STDAC. ; Label accums

    amt==3 ; Num of chars to handle
    depth==amt+1 ; Stack size + protector getmsg: asciz /Enter file name to write to: / ; User prompts and messages getchr: asciz /Enter three numbers, each followed by \r\n.
    /
    stack: block depth ; Storage place for user nums jfn: 0 ; Job File Number for outfile

    subttl Main
    start: reset ; Working with files, RESET
    move p, [iowd depth, stack] ; Setup stack w/OF guard
    movei cx, amt ; Load char amount to indexer

    subttl Get User Numbers
    ; First get the user's numbers onto the stack for later output
    hrroi t1, getchr ; Prompt heading
    psout%
    inloop: move t1, [.priin] ; Signal input is keyboard
    movei t3, 12 ; Base is decimal
    nin% ; Get a number
    erjmp [TMSG (Error on NIN%. Exiting.
    )
    haltf%] ; Exit w/mesg if no number
    push p, t2 ; Save number on the stack
    sojg cx, inloop ; Get all 'amt' numbers

    subttl Open File
    ; Load ac1 with get JFN flags short, update file, name is supplied by TTY
    ; Load ac2 with place to get from
    hrroi t1, getmsg ; Load user prompt for get file
    psout%
    ; GJ%SHT : short form GJ%NEW : Create a file, if exists, returns +1
    ; GJ%OLD : file must exist GJ%FOU : Update a file (new generation)
    ; GJ%FNS : user supplies file name at the terminal prompt
    move t1, [GJ%SHT+GJ%FNS] ; Load JFN flags
    move t2, [.priin,,.priou] ; Input from TTY
    gtjfn%
    erjmp [TMSG (Error on getting JFN. Exiting.
    )
    haltf%] ; Exit w/message if no JFN
    movem t1, jfn ; Stash JFN for later
    move t2, [OF%APP+44B5] ; Open append, 36-bit dec
    openf% ; JFN should still be in T1
    erjmp [TMSG (Error on file opening. Exiting.
    )
    haltf%] ; Exit w/mesg if can't open

    subttl Write File
    ; Take numbers off the stack and put them into the open file. The loop
    ; counter is also the indexer into the stack because the stack has the
    ; numbers on it backwards. The exercise never says to write to the file
    ; in order, but this is what a user would expect so this is what this
    ; program does.
    setz cx, ; Zero loop + indexer
    ouloop: move t1, jfn ; Load JFN
    move t2, stack(cx) ; Load number off stack
    bout% ; Write to file
    cail cx, amt - 1 ; Did all numbers?
    jrst close ; Done. Now close file.
    addi cx, 1 ; Increment cnt for next num
    jrst ouloop ; and loop again

    subttl Close File
    close: move t1, jfn ; Load JFN
    closf%
    erjmp [TMSG (Error on closing file!
    )
    haltf%] ; Exiting, but no restart

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program
    lit ; Expand literals
    end start ; Assembler is done


    And here's readb to read such files:


    comment $
    Test program to read files written with BOUT% and byte size of 44 octal (36 bits in decimal, opened OF%WR+44B5 ). Files written in this way can't be
    TYPEd at EXEC. This program reads the entries to make sure they are
    being written properly.
    $
    title readb
    search monsym, macsym ; Get symbols from .UNV
    STDAC. ; Label accums

    ; Max to read, or stop if EOF occurs first
    amt==50 ; Read this many entries
    jfn: 0 ; Store Job File Number

    subttl Main
    start: reset ; Working with files, RESET
    TMSG (*** [READ B]inary file utility ***
    ) ; Heading
    TMSG ( Enter the file name to check: )
    move t1, [GJ%SHT+GJ%OLD+GJ%FNS] ; Short, exists, input TTY
    move t2, [.priin,,.priou] ; Get JFN reads from TTY
    gtjfn%
    erjmp [TMSG (Error on getting JFN. Exiting.
    )
    haltf%] ; Exit if can't get JFN
    movem t1, jfn ; Stash JFN for later
    open: move t2, [OF%RD+44B5] ; 44 (36 decimal) for numbers
    openf%
    erjmp [TMSG (Error on file open. Exiting.
    )
    haltf%] ; Bail if can't open file
    movei cx, amt ; Loop indexer
    loop: move t1, jfn ; Load Job File Number
    bin% ; Read entry
    skipn t2 ; Is char a NULL?
    jsr chkeof ; Sure, but is it EOF?
    move t1, [.priou] ; Else ready NOUT%
    movei t3, 12 ; Display decimal base
    nout%
    erjmp [TMSG (Error on NOUT%. Exiting.
    )
    haltf%] ; Exit if can't disp number
    TMSG (
    ) ; \r\n spacer
    sojg cx, loop ; Loop to get other chars

    close: move t1, jfn ; Reload JFN
    closf% ; Close file
    erjmp [TMSG (Error on closing file!
    )
    haltf%] ; Mesg + exit
    done: haltf% ; Exit to monitor
    jrst start ; Run again?

    chkeof: 0 ; Save return addr space
    gtsts% ; Get file status
    tlne t2, (GS%EOF) ; Bit 8 set?
    jrst close ; Yes, close, leave
    setz t2, ; No, it's just a NULL
    jrst @chkeof ; So let it be (replace)

    lit ; 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Wed Aug 12 21:08:29 2026
    From Newsgroup: alt.lang.asm

    chau3.mac

    Write two text files and then merge them into a third. Delete the first
    2 originals.

    This program was harder than it looks because, although the text says to
    use DELF% to delete them, it doesn't tell you that once you close them
    it releases the JFN, which is needed to delete them. If you don't close
    them and delete them, the JFN doesn't release (the OS seems to free the
    JFNs anyway after program execution). The solution is to tell CLOSF% to
    not release the JFN - which the text never explains. Reading the monitor reference manual, we can see how to do this. There's a macro to load one
    side of the accumulator MOVX(), or I could have used movsi - which the
    text also doesn't mention.

    There's an error handler from the Gorin text and some fancy literals to
    find if a NULL is an EOF NULL or just a plain-jane NULL.


    @type 1.dat,2.dat
    1.DAT.1

    00100 hello world.

    2.DAT.1

    00100 hello all you eels!

    @exec chau3
    MACRO: chau3
    LINK: Loading
    [LNKXCT CHAU3 execution]
    Getting first file's JFN.
    Getting results (3.dat) file JFN.
    Getting second (2.dat) input file JFN.
    1.dat and 2.dat written to 3.dat. 1,2 deleted.
    @type 3.dat
    hello world.
    hello all you eels!

    Yes, the line numbers put in by EDIT disappear somewhere along the line.

    comment $
    Using the system editor, create two files. Write a program that will
    merge the files into a new file (simply place one after the other).
    Use the JSYS DELF% to delete the other two files.
    $
    title chau3
    search monsym, macsym ; Get symbols from .UNVs
    lall ; Expand macros
    STDAC. ; Label accums

    jfn1: 0 ; Job File Numbers
    jfn2: 0 ; for 1st and 2nd files
    jfn3: 0 ; Results file JFN

    start: reset ; Working with files, RESET
    TMSG (Getting first file's JFN.
    ) ; Announce workenings
    ; Open first file, open results file. Read from first and write into
    ; results file. Close first file. Open second file. Append to results
    ; file. Close all files remaining open.
    move t1, [GJ%SHT+GJ%OLD] ; Short form, file exists
    hrroi t2, [asciz /1.dat/] ; Hardcoded filename, 1.dat
    gtjfn%
    erjmp errorf ; Handle file error
    movem t1, jfn1 ; Save JFN 1
    TMSG (Getting results (3.dat) file JFN.
    )
    move t1, [GJ%SHT+GJ%FOU] ; Results file gets new gen
    hrroi t2, [asciz /3.dat/] ; Results file, 3.dat
    gtjfn%
    erjmp errorf ; Error handler for file err
    movem t1, jfn3 ; Save JFN 3, results file

    ; Open first file
    move t1, jfn1 ; Open first file to read
    move t2, [OF%RD+7B5] ; in 7bit ASCII to read
    openf%
    erjmp errorf ; Error handler for file errs

    ; Open results file
    move t1, jfn3 ; JFN for results file
    move t2, [OF%APP+7B5] ; Open append in 7bit ASCII
    openf%
    erjmp errorf

    ; Read from first and write into results file
    rd1: move t1, jfn1 ; Read from first file
    bin% ; Read char from file
    skipn t2 ; Is it NULL?
    jrst [gtsts% ; Yes, but is it EOF?
    tlne t2, (GS%EOF) ; Bit 8 set?
    jrst rd1clo ; Yes, EOF, close
    setz t2, ; No, just a plain NULL
    jrst rd1.1] ; Continue on
    rd1.1: move t1, jfn3 ; JFN for results file
    bout% ; Write T2 char to result file
    jrst rd1 ; Loop until EOF on file 1
    ; CLOSF% needs to be told not to release the JFN (it normally does) because
    ; we still need it to delete the file. del+close should release the JFN. rd1clo: MOVX (t1,CO%NRJ) ; Don't release JFN
    hrr t1, jfn1 ; because needed for delf%
    closf% ; closf% normally releases
    erjmp errorf
    move t1, jfn1 ; Make sure JFN set
    delf% ; Delete file
    erjmp errorf
    ; The text says 'delete' but this would be for releasing JFN without del
    ; rljfn% ; Release JFN 1
    ; erjmp .+1 ; Ignore error

    ; Get file 2 JFN, read from second file and write into results file
    TMSG (Getting second (2.dat) input file JFN.
    )
    move t1, [GJ%SHT+GJ%OLD] ; Short form, file exists
    hrroi t2, [asciz /2.dat/] ; Hardcoded filename, 2.dat
    gtjfn% ; Get JFN for file 2
    erjmp errorf ; Handle file error
    movem t1, jfn2 ; Save JFN 2

    ; Open second file
    move t1, jfn2 ; Open second file to read
    move t2, [OF%RD+7B5] ; in 7bit ASCII to read
    openf%
    erjmp errorf ; Error handler for file errs

    ; Read from second file and write into results file
    rd2: move t1, jfn2 ; Read from second file
    bin% ; Read char from file
    skipn t2 ; Is it NULL?
    jrst [gtsts% ; Yes, but is it EOF?
    tlne t2, (GS%EOF) ; Bit 8 set?
    jrst rd2clo ; Yes, EOF, close
    setz t2, ; No, just a plain NULL
    jrst rd2.1] ; Continue on
    rd2.1: move t1, jfn3 ; JFN for results file
    bout% ; Write T2 char to result file
    jrst rd2 ; Loop until EOF on file 2 rd2clo: MOVX (t1,CO%NRJ) ; Could have used MOVSI to
    hrr t1, jfn2 ; load left half of ac1
    closf%
    erjmp errorf
    move t1, jfn2 ; Make sure JFN 2 here
    delf% ; Del file, release JFN
    erjmp errorf
    ; To release JFN without delete, use below code
    ; rljfn% ; Release JFN 2
    ; erjmp .+1 ; Ignore errors

    ; Done with files at this point. Close.
    move t1, jfn3 ; Reference results file
    closf% ; Close file
    erjmp errorf
    rljfn% ; Release results JFN
    erjmp .+1 ; Punt on error

    ; Can't use () with TMSG when using commas.
    done: TMSG <1.dat and 2.dat written to 3.dat. 1,2 deleted.
    ; Announce results
    haltf% ; Exit to monitor
    jrst start ; Run program again?

    ; Error handling example from Gorin text
    errorf: TMSG (Error: )
    esout% ; Clear any type-ahead
    move t1, [.priou] ; Error to terminal
    hrloi t2, .fhslf ; This fork, most recent err
    setz t3, ; No byte count limit
    erstr% ; Convert last error to str
    jfcl
    jfcl ; Two possible error returns
    haltf% ; Exit but no restart

    lit ; Debugger to expand literals
    end start
    --
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Fri Aug 14 20:50:50 2026
    From Newsgroup: alt.lang.asm

    chau4.mac

    Another hard program. This program requires sorting, but the text never discusses how to do this. What does bubble sort look like in assembler?
    In C, we can use the array indexes and the for loops cycle through all
    the numbers so that we can compare them. Here, there's no such
    thing. The "sort" subroutine is what took all the time in this one. EXCH doesn't work on moving data in indexed memory, so I had to move the
    values to accumulators, EXCH, and then use MOVEM to the proper place in
    the buffer. This buffer later gets written to the results file. Assembly
    makes simple things hard.

    The program can be expanded to larger files, but the requirement had 3
    numbers per file so that's what this one does. Negative numbers are
    handled correctly. "amount" can be increased, but both input files
    should be the same length. That is, 6 and 6 or 8 and 8, etc but not 5
    and 6. The text doesn't say what should happen in this case. My solution
    does not require that the input files be in ascending order.

    @exec chau4
    MACRO: chau4
    LINK: Loading
    [LNKXCT CHAU4 execution]
    Reading first file...
    Reading second file...
    Writing to data file...

    @type 1.dat,2.dat,3.dat
    1.DAT.3

    3 -3 10


    2.DAT.2

    2 7 9

    3.DAT.5

    -3 2 3 7 9 10

    comment $
    Using the system editor, create two files containing numbers sorted in ascending order. Write a program that will merge the numbers into one
    sorted file.

    Example:
    file 1: 3 8 10
    file 2: 2 7 9
    file 3: 2 3 7 8 9 10
    $
    title chau4
    search monsym, macsym ; Use .UNV symbols
    lall ; Expand macros
    STDAC. ; Label accums

    amount==3 ; Number of numbers per file
    ssize==2 ; Size of stack w/OF guard stack: block ssize ; Space for stack

    ; File names are hardcoded for simplicy: 1.dat, 2.dat, results = 3.dat.
    ; MUST keep file2 after file1 because this is the memory space searched
    ; by the sorting routine.
    file1: block amount ; Space for file 1's numbers file2: block amount ; Likewise for file 2's
    jfn1: 0 ; JFN storage for files
    jfn2: 0
    jfn3: 0

    subttl Main
    start: reset ; Reset when using files
    move p, [iowd ssize, stack] ; Set up stack
    pushj p, get1 ; Load first file to memory
    pushj p, get2 ; Load second to memory
    pushj p, sort ; Sort the value in memory
    pushj p, write ; Write out data file
    done: haltf% ; Exit to monitor
    jrst start ; Restartable program


    ; Subroutine to read to memory the first file, then close it.
    subttl Get First File
    get1: TMSG (Reading first file...
    ) ; Print heading
    move t1, [GJ%SHT+GJ%OLD] ; Short form, file exists
    move t2, [point 7, [asciz /1.dat/]] ; Select this file
    gtjfn% ; Get JFN
    erjmp [hrroi t1, [asciz /Error on getting JFN 1. Exiting./]
    psout%
    haltf%] ; Exit w/message on error
    movem t1, jfn1 ; Stash returned JFN

    ; Open file 1 for reading
    move t2, [OF%RD+7B5] ; Open 7bit ASCII
    openf% ; JFN still in T1, open
    erjmp [hrroi t1, [asciz /Error on file 1 open. Exiting./]
    psout%
    haltf%] ; Exit on open error

    setz cx, ; Indexer, zero-idx nums
    rd1: move t1, jfn1 ; Reload JFN
    movei t3, 12 ; Decimal base
    nin% ; Read first number
    erjmp [hrroi t1, [asciz /Error on NIN%, file 1. Exiting./]
    psout%
    haltf%] ; Exit on can't NIN%
    movem t2, file1(cx) ; Else save num to memory
    addi cx, 1 ; Increment counter
    caig cx, amount - 1 ; Loop while <= amount -1
    jrst rd1 ; Get more chars

    clos1: move t1, jfn1 ; Close file 1
    closf%
    erjmp [hrroi t1, [asciz /Error on file 1 close./]
    psout%
    haltf%] ; Exit w/message
    popj p, ; Otherwise return to caller


    ; Subroutine to read to memory the second file, then close it.
    subttl Get Second File
    get2: TMSG (Reading second file...
    ) ; Print heading
    move t1, [GJ%SHT+GJ%OLD] ; Short form, file exists
    move t2, [point 7, [asciz /2.dat/]] ; Select this file
    gtjfn% ; Get JFN
    erjmp [hrroi t1, [asciz /Error on getting JFN 2. Exiting./]
    psout%
    haltf%] ; Exit w/message on error
    movem t1, jfn2 ; Stash returned JFN

    ; Open file 2 for reading
    move t2, [OF%RD+7B5] ; Open 7bit ASCII
    openf% ; JFN still in T1, open
    erjmp [hrroi t1, [asciz /Error on file 2 open. Exiting./]
    psout%
    haltf%] ; Exit on open error

    setz cx, ; Looper and indexer
    rd2: move t1, jfn2 ; Reload JFN
    movei t3, 12 ; Decimal base
    nin% ; Read first number
    erjmp [hrroi t1, [asciz /Error on NIN%, file 2. Exiting./]
    psout%
    haltf%] ; Exit on can't NIN%
    movem t2, file2(cx) ; Else save num to memory
    addi cx, 1 ; Increment counter
    caig cx, amount - 1 ; Loop while <= amount -1
    jrst rd2 ; Get more chars

    clos2: move t1, jfn2 ; Close file 2
    closf%
    erjmp [hrroi t1, [asciz /Error on file 2 close./]
    psout%
    haltf%] ; Exit w/message
    popj p, ; Otherwise return to caller


    ; Sort the values in memory using a bubble sort algorithm. The block of
    ; memory searched is file1 and file2 right after that.
    ; Q2 = outer loop indexer P1 = Outer number value derived off Q2 index
    ; Q1 = inner loop indexer P2 = inner number value derived off Q1 index
    ; Get num and next term to compare in P1 and P2. Compare. Swap these
    ; values if greater so that they are lesser. Write back P1 and P2 to
    ; their memory area at file1+file2. file1+file1 area is (amount * 2).
    subttl Sort Array
    sort: setz q2, ; Controls outer loop
    outer: move p1, file1(q2) ; Term 1 number
    setz q1, ; Controls inner loop
    inner: move p2, file1(q1) ; Term +1 number
    camge p1, p2 ; Term 1 <= term +1
    jrst [exch p1, p2 ; else swap terms
    movem p1, file1(q2) ; And move out to memory
    movem p2, file1(q1) ; Likewise
    jrst outer] ; Short out of loop
    addi q1, 1 ; Increment term+1 number
    caig q1, amount * 2 - 1 ; Zero-indexed amount of
    jrst inner ; numbers to do, total
    addi q2, 1 ; Inc current term num
    caig q2, amount * 2 - 1 ; Determines outer loop
    jrst outer ; Check nums on next pass
    popj p, ; Return to caller


    ; Subroutine to write out results to the data file, 3.dat.
    subttl Write Results File
    write: TMSG (Writing to data file...
    ) ; Print heading
    move t1, [GJ%SHT+GJ%FOU] ; Short form, new generation
    move t2, [point 7, [asciz /3.dat/]] ; Select this file
    gtjfn% ; Get JFN
    erjmp [hrroi t1, [asciz /Error on getting JFN 3. Exiting./]
    psout%
    haltf%] ; Exit w/message on error
    movem t1, jfn3 ; Stash returned JFN

    ; Open results file for writing
    move t2, [OF%WR+7B5] ; Open 7bit ASCII
    openf% ; JFN still in T1, open
    erjmp [hrroi t1, [asciz /Error on results file open. Exiting./]
    psout%
    haltf%] ; Exit on open error
    setz cx, ; Looper and indexer
    wrt: move t1, jfn3 ; Reload JFN
    move t2, file1(cx) ; Read from file1/2 block
    movei t3, 12 ; Decimal base
    nout% ; Write next number
    erjmp [hrroi t1, [asciz /Error on NOUT% results. Exiting./]
    psout%
    haltf%] ; Exit on can't NOUN%
    movei t2, .CHSPC ; Place a space separator
    bout% ; Print to file
    addi cx, 1 ; Increment counter
    caig cx, amount * 2 - 1 ; Count is for both file1/2
    jrst wrt ; Write more chars

    clos3: move t1, jfn3 ; Close file 3
    closf%
    erjmp [hrroi t1, [asciz /Error on results file close./]
    psout%
    haltf%] ; Exit w/message
    popj p, ; Otherwise return to caller


    lit ; 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Mon Aug 17 21:01:18 2026
    From Newsgroup: alt.lang.asm

    chau5.mac

    This program was much easier than the last. Open a file and replace
    characters according to the user's wishes. This should be the last
    exercise in Chapter 10, and then I'm on to Chapter 11.

    @type 4.dat
    This is some text for Chapter 10,
    user exercise number 5.
    @exec chau5
    MACRO: chau5
    LINK: Loading
    [LNKXCT CHAU5 execution]
    Enter the character to search/replace: e
    Enter character to replace it with: 3
    Enter file to search/replace: 4.dat
    @type 4.dat
    This is som3 t3xt for Chapt3r 10,
    us3r 3x3rcis3 numb3r 5.

    comment $
    Write a simple editor program with a substitue command to replace one
    character with another. Determine the file to be edited at run time.
    $
    title chau5
    search monsym, macsym
    lall ; Expand macros
    STDAC. ; Label accums

    ssize==2 ; Stack size w/OF guard

    .PSECT DATA,1001000 ; Must set loc with DATA .PSECT orgchr: 0 ; Origainl character
    newchr: 0 ; Character to replace it with jfn: 0 ; JFN for file to edit
    stack: block ssize ; Place for stack

    .PSECT CODE,1002000 ; Can't use /RONLY with DDT
    subttl Main
    start: reset ; Working with files, RESET
    move p, [iowd ssize, stack] ; Set up the stack
    pushj p, getchr ; Get the user's characters
    pushj p, repchr ; Open file, do replacement

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program


    ; Get both the original character and the new, replacement character from
    ; the user. Stash these to memory for use later in the program.
    subttl Get Characters
    getchr: TMSG (Enter the character to search/replace: )
    pbin% ; Read char to T1
    movem t1, orgchr ; Stash for later
    TMSG (
    Enter character to replace it with: )
    pbin%
    movem t1, newchr ; Stash replacement char
    popj p, ; Return to caller


    ; Open the user's file and replace the chosen character with the new
    ; character, both of which were previously gotten.
    subttl Replace Characters
    repchr: TMSG (
    Enter file to search/replace: ) ; TMSG clobbers T1! Prompt 1st

    move t1, [GJ%SHT+GJ%FNS+GJ%OLD] ; File must exist
    ; GJ%SHT : short form GJ%NEW : Create a file, if exists, returns +1
    ; GJ%OLD : file must exist GJ%FOU : Update a file (new generation)
    ; GJ%FNS : user supplies the file name w/keyboard at exec time

    move t2, [.priin,,.priou] ; Input file name from TTY
    gtjfn%
    erjmp errorf ; File-related error handler
    movem t1, jfn ; Stash Job File Number
    move t2, [OF%RD+OF%WR+7B5] ; Open file r/w, 7bit ASCII
    openf%
    erjmp errorf ; Go to error handler

    ; By now the file is open for read/write. Next, search/replace characters.
    movni t3, 1 ; Set file pointer to -1 rdloop: move t1, jfn ; Fetch JFN
    addi t3, 1 ; Increment file pointer
    rin% ; Get next byte
    skipn t2 ; Is it a null?
    jsr check ; Yes, check for EOF condition
    camn t2, orgchr ; Is this the original char?
    jrst [move t2, newchr
    rout%
    jrst update] ; Yes, replace it
    update: jumpa rdloop ; And process rest of file

    ; Done, close file and release JFN
    move t1, jfn ; Fetch JFN
    closf% ; This should also release JFN
    erjmp errorf ; Handle error
    popj p,

    ; Borrow error handler from Gorin text. Does not return (exits).
    errorf: TMSG (Error: )
    esout% ; Clear any type-ahead
    move t1, [.priou] ; Error to terminal
    hrloi t2, .fhslf ; This fork, most recent err
    setz t3, ; No byte count limit
    erstr% ; Convert last error to str
    jfcl
    jfcl ; Two possible error returns
    haltf% ; Exit but no restart

    ; Routine to check if NULL is EOF NULL or just plain (?) NULL.
    check: 0 ; Return addr space
    gtsts% ; Get file status
    tlne t2, (GS%EOF) ; Bit 8 set?
    jrst done ; Yes, done
    setz t2, ; No, put back NULL
    jrst @check ; Return to caller

    lit ; Expand any 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Sun Aug 23 19:55:31 2026
    From Newsgroup: alt.lang.asm

    chbu1.mac

    Chapter 11 is about interrupts. It's a difficult chapter partly because
    of the material, and partly because of the fact that 2/3 of the example programs do not assemble. One does. The others use macros constructed
    from examples spread out over the 3 previous chapters. This wouldn't be
    the first time there were errors in the text, but these macros nest 3-4
    deep and I can't debug them because I don't know what I'm doing anyway
    (having just started interrupt study). Thus my solutions don't use the
    macros. There's also an issue with using PSECTs with interrupts; it
    doesn't see possible. Of course, the error messages don't directly tell
    you this.

    There is an EOF handler, but this exercise asks the user to write one
    while searching a file for a character.

    @recall 6
    exec chbu1
    LINK: Loading
    [LNKXCT CHBU1 execution]
    Enter the character to search for: ?
    Enter the file name you want to search in: 4.dat
    Character found at position 17
    @cont
    Enter the character to search for: x
    Enter the file name you want to search in: 4.dat
    Not found. EOF reached.
    @type 4.dat
    Who's a good eel?


    comment $
    White a program that will search lineraly through a file for a character.
    The program should supply both the file and the character at run time.
    If the character is present, print its numerical position. If the
    character is not present, trap the end-of-file interrupt and print
    "not found".

    Notes: Assuming "position" means "first occurance thereof".
    Assuming "at run time" means "ask the user for the data".
    $
    title chbu1
    search monsym, macsym
    lall ; Expand macros
    STDAC. ; Standard label accums

    ssize==3 ; Stack size w/OF guard
    define INC (acc<cx>) < ; Increment an accumulator
    addi acc, 1 ; Defaults to CX, my usual
    ; indexer/counter for loopage

    ; Neither .PSECT for code or data can be set when working with interrupts
    ; for reasons unknown to me else error (and crash):
    ; ?SIR JSYS invoked from non-zero section
    ; .PSECT DATA,1001000 ; Must set loc with DATA .PSECT fndchr: 0 ; Char we're supposed to find jfn: 0 ; Job file number of work file poschr: 0 ; Zero-indexed char position

    ; Channel table. Channels number 0-35, 36 total. Your routine must set in
    ; in the slot of the channel you will handle. See Table 11-1, Longo text,
    ; for assignable channels and their numbers.
    chntab: 0 ; Zero chan 0, not using
    0 ; Zero chan 1, not using
    0 ; Likewise, etc
    0
    0
    0
    0
    0
    0
    0
    2,,eof ; .ICEOF sits on chan 10
    block chntab+^D36-. ; Fill remainder of 36 words

    ; Level table. Must have 3 slots, zero non-used priorities
    levtab: 0 ; No priority 1
    pc2 ; Level two slot
    0 ; No level 3 slot
    pc2: 0 ; Save space for program cntr

    stack: block ssize ; Space for stack

    ; .PSECT CODE,1002000 ; Can't use /RDONLY w/DDT
    subttl Main
    start: reset ; Working with files, reset
    move p, [iowd ssize, stack] ; Set up the stack
    move t1, [.FHSLF] ; Set up interrupt handlers
    move t2, [levtab,,chntab] ; Point to needed tables
    sir% ; Good SIR%, please give ints
    move t1, [.FHSLF] ; Fork handle self
    eir% ; Enable Interrupts
    move t1, [.FHSLF]
    move t2, [1B<.ICEOF>] ; Select to handle EOF
    aic% ; Activate Interrupt Channel
    pushj p, getchr ; Get the user's character
    pushj p, getfil ; Get JFN, open work file
    pushj p, schfil ; Search the file
    pushj p, clofil ; Close file
    done: haltf% ; Exit to monitor
    jrst start ; Restartable program


    ; -[Support Routines]------------------------------------------
    subttl Support Routines
    ; Get character from the user to search for, from the keyboard
    getchr: TMSG (Enter the character to search for: )
    pbin% ; Read user's char to T1
    movem t1, fndchr ; Stash for later
    popj p, ; Return to caller

    ; Get JFN and open the user-specified file for reading operations
    getfil: TMSG (
    Enter the file name you want to search in: )
    move t1, [GJ%SHT+GJ%OLD+GJ%FNS] ; Exists, key-in, shortform
    ; GJ%SHT : short form GJ%NEW : Create a file, if exists, returns +1
    ; GJ%OLD : file must exist GJ%FOU : Update a file (new generation)
    ; GJ%FNS : user supplies the file name w/keyboard at exec time
    move t2, [.priin,,.priou] ; Input file name from TTY
    gtjfn%
    erjmp errorf ; File-related error handler
    movem t1, jfn ; Stash JFN for later use
    move t2, [OF%RD+7B5] ; Open read, 7bit ASCII
    openf%
    erjmp errorf ; Handle file error
    popj p, ; Return to caller

    ; Search the open file for the indicated character. poschr is actually
    ; zero-indexed; convert to one-indexed as to be read by a human
    schfil: movni t3, 1 ; Set file pointer to -1 rdloop: move t1, jfn ; Fetch Job File Number
    INC (t3) ; Increment file pointer
    rin% ; Get next byte
    camn t2, fndchr ; Is it what we're looking for?
    jrst [ movem t3, poschr ; Yes, note position
    TMSG (Character found at position )
    move t1, [.priou] ; Ready NOUT%
    move t2, poschr ; Load position
    INC (t2) ; Convert to one-indexed
    movei t3, 12 ; Print in decimal base
    nout%
    erjmp [hrroi t1, [asciz /Error on NOUT%. Exiting./]
    psout%
    haltf%] ; Halt, no restart
    jrst endlop] ; Finish up and return
    jumpa rdloop ; Loop, let inter handle EOF endlop: popj p, ; Return to caller

    ; Close the work file
    clofil: move t1, jfn ; Fetch JFN from earlier
    closf% ; Should also release JFN
    erjmp errorf ; Handle error (no return)
    popj p, ; Doesn't actually return
    ; on error, but does on success

    ; Interrupt handler for end-of-file channel condition. File should still
    ; be open at this point. Close it.
    subttl Interrupt Handler
    eof: TMSG (Not found. EOF reached.)
    pushj p, clofil ; Call close file routine
    haltf% ; Exit to monitor

    ; Borrow error handler from Gorin text. Does not return (exits).
    subttl File Error Handler
    errorf: TMSG (Error: )
    esout% ; Clear any type-ahead
    move t1, [.priou] ; Error to terminal
    hrloi t2, .FHSLF ; This fork, most recent err
    setz t3, ; No byte count limit
    erstr% ; Convert last error to str
    jfcl
    jfcl ; Two possible error returns
    haltf% ; Exit but no restart

    lit ; 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Mon Aug 24 21:25:44 2026
    From Newsgroup: alt.lang.asm

    chbu2.mac

    Now I have the general idea down about how interrupts work, working with
    them isn't so hard. I'm still confused about this SIR%/XSIR% bit,
    because I found an example, it assembles, yet doesn't work. It does
    something like:
    movei t2, [exp 3, levtab, chntab]
    xsir%

    but the levtab might be different. No explaination about where the
    magical, mystery "3" comes from. If a picture is worth a thousand words,
    then one example in computer science is probably worth 10,000. Alas,
    there are none. Anyway, this program demos an interrupt on a stack
    overflow.

    @exec chbu2
    MACRO: chbu2
    LINK: Loading
    [LNKXCT CHBU2 execution]
    Starting demo with item count at 5
    Pushing item on the stack: 5
    Pushing item on the stack: 4
    Pushing item on the stack: 3
    Pushing item on the stack: 2
    Pushing item on the stack: 1
    Thy stack runneth over!

    comment $
    Set up a stack and the necessary conditions to trap a stack pushdown
    overflow. Test your program by using PUSH too often.
    $
    title chbu2
    search monsym, macsym
    lall
    STDAC. ; Label accums and such

    ; Change this to demo the overflow after the set number. Ex: 3, 5, etc
    ssize==5 ; Stack size

    ; See Monitor Ref Calls Manual table for symbols to channel table
    chntab: 0 ; Channel table for SIR%
    0 ; Zero unhandled channels
    0 ; Chan 2, no
    0 ; 3 no and so on...
    0
    0
    0
    0
    0 ; Channel 8, no
    2,,ovrflw ; Handle .ICPOV, chan 9
    block chntab+^D36-. ; Fill remainder of 36 words

    levtab: 0 ; Priority 1
    pc2 ; Priority 2, handle
    0 ; Priority 3
    pc2: 0 ; Program counter save space stack: block ssize ; Space for stack

    subttl Main
    start: reset
    move p, [iowd ssize, stack] ; Set up stack
    move t1, [.FHSLF] ; Fork, yourself
    move t2, [xwd levtab, chntab] ; Set up interrupt handlers
    sir% ; Please SIR% give INTS
    move t1, [.FHSLF]
    eir% ; Turn them on
    move t1, [.FHSLF]
    move t2, [1B<.ICPOV>] ; Indicate to handle this one
    aic% ; Activate Int Chan
    TMSG (Starting demo with item count at )
    move t1, [.priou] ; Ready NOUT%
    movei t2, ssize ; Whatever it currently is
    movei t3, 12 ; Decimal base
    nout%
    erjmp [hrroi t1, [asciz /Error on NOUT%. Exiting./]
    psout%
    haltf%] ; Exit on error.
    TMSG (
    ) ; Tidy output with \r\n
    movei cx, ssize ; Use loop to demo stack
    loop: TMSG (Pushing item on the stack: )
    move t1, [.priou] ; Ready NOUT%
    move t2, cx
    movei t3, 12 ; Decimal base output
    nout%
    erjmp [hrroi t1, [asciz /Error on NOUT%. Exiting./]
    psout%
    haltf%] ; Just exit on error
    push p, cx ; Put stuff on stack
    TMSG (
    ) ; Emit newline to tidy output
    sojg cx, loop ; Loop to fill stack
    TMSG (Stack overflow demo has concluded.)
    done: haltf%
    jrst start ; Restart if you like


    ; -[Support routine]--------------------------------
    ; Handler for .ICPOV, stack overflow event
    subttl Stack overflow handler
    ovrflw: TMSG (
    Thy stack runneth over!
    ) ; Just display a message?
    debrk% ; Return from interrupt

    lit ; Expand literals in DDT
    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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Tue Aug 25 16:04:36 2026
    From Newsgroup: alt.lang.asm

    chbu3.mac chbu4.mac

    More interrupt usage. The first program puts a timer (via TIMER%) on the program from exercise 1. I don't have an audible bell so I use a visual
    bell it its place. The second program demos a keyboard interrupt.

    @type 4.dat
    Who's a good eel?

    @exec chbu3
    LINK: Loading
    [LNKXCT CHBU3 execution]
    Enter the character to search for:
    Bell!
    a
    Enter the file name you want to search in: 4.dat
    Character found at position 7


    comment $
    Set up a TIMER% interrupt so that the bell will ring every 30 seconds
    while a program is running. (For instance, insert the TIMER% routine
    into your program for exercise 1.)

    Notes: There's no bell on this system, print "Bell!" instead. The
    visual bell can disrupt the file name (or character) entry, so type
    it right after the bell or before it trips.
    $
    title chbu3
    search monsym, macsym
    lall ; Expand macros
    STDAC. ; Standard label accums

    ssize==3 ; Stack size w/OF guard
    elptim==1000*30 ; Timer for bell, in millisec

    define INC (acc<cx>) < ; Increment an accumulator
    addi acc, 1 ; Defaults to CX, my usual
    ; indexer/counter for loopage

    define SETIME (mstime) < ; Set a timer for 'mstime' MS
    move t1, [xwd .FHSLF, .TIMEL] ; Time self, elapsed run time
    movei t2, mstime ; Elapsed time value in MS
    setz t3, ; Send interp on chan 0
    timer% ; Set timer
    erjmp errorf ; Use error handler
    >

    fndchr: 0 ; Char we're supposed to find jfn: 0 ; Job file number of work file poschr: 0 ; Zero-indexed char position

    ; Channel table. Channels number 0-35, 36 total. Your routine must set
    ; the slot of the channel you will handle. See Table 2-12 Mon Ref Manual
    ; for assignable channels and their numbers.
    chntab: 3,,bell ; Chan 0 goes to bell, prio 3
    0 ; Zero chan 1, not using
    0 ; Likewise, etc
    0
    0
    0
    0
    0
    0
    0
    2,,eof ; .ICEOF sits on chan 10
    block chntab+^D36-. ; Fill remainder of 36 words

    ; Level table. Must have 3 slots, zero non-used priorities.
    levtab: 0 ; No priority 1
    pc2 ; Level 2 slot
    pc3 ; Level 3 slot
    pc2: 0 ; Save space for program cntr pc3: 0 ; PC save for lvl 3

    stack: block ssize ; Space for stack

    subttl Main
    start: reset ; Working with files, reset
    move p, [iowd ssize, stack] ; Set up the stack

    ; This section sets up the interrupt handlers
    move t1, [.FHSLF] ; Set up interrupt handlers
    move t2, [xwd levtab, chntab] ; Point to needed tables
    sir% ; Good SIR%, please give ints
    move t1, [.FHSLF] ; Fork handle self
    eir% ; Enable Interrupts
    move t1, [.FHSLF]
    move t2, [1B<.ICEOF>+1B<0>] ; Select handle EOF+chan 0
    aic% ; Activate Interrupt Channel
    SETIME (elptim) ; Set a timer in milliseconds

    ; File/character handling
    pushj p, getchr ; Get the user's character
    pushj p, getfil ; Get JFN, open work file
    pushj p, schfil ; Search the file
    pushj p, clofil ; Close file

    done: haltf% ; Exit to monitor
    jrst start ; Restartable program


    ; -[Support Routines]------------------------------------------
    subttl Support Routines
    ; Get character from the user to search for, from the keyboard
    getchr: TMSG (Enter the character to search for: )
    pbin% ; Read user's char to T1
    movem t1, fndchr ; Stash for later
    popj p, ; Return to caller

    ; Get JFN and open the user-specified file for reading operations
    getfil: TMSG (
    Enter the file name you want to search in: )
    move t1, [GJ%SHT+GJ%OLD+GJ%FNS] ; Exists, key-in, shortform
    ; GJ%SHT : short form GJ%NEW : Create a file, if exists, returns +1
    ; GJ%OLD : file must exist GJ%FOU : Update a file (new generation)
    ; GJ%FNS : user supplies the file name w/keyboard at exec time
    move t2, [.priin,,.priou] ; Input file name from TTY
    gtjfn%
    erjmp errorf ; File-related error handler
    movem t1, jfn ; Stash JFN for later use
    move t2, [OF%RD+7B5] ; Open read, 7bit ASCII
    openf%
    erjmp errorf ; Handle file error
    popj p, ; Return to caller

    ; Search the open file for the indicated character. poschr is actually
    ; zero-indexed; convert to one-indexed as to be read by a human
    schfil: movni t3, 1 ; Set file pointer to -1 rdloop: move t1, jfn ; Fetch Job File Number
    INC (t3) ; Increment file pointer
    rin% ; Get next byte
    camn t2, fndchr ; Is it what we're looking for?
    jrst [ movem t3, poschr ; Yes, note position
    TMSG (Character found at position )
    move t1, [.priou] ; Ready NOUT%
    move t2, poschr ; Load position
    INC (t2) ; Convert to one-indexed
    movei t3, 12 ; Print in decimal base
    nout%
    erjmp [hrroi t1, [asciz /Error on NOUT%. Exiting./]
    psout%
    haltf%] ; Halt, no restart
    jrst endlop] ; Finish up and return
    jumpa rdloop ; Loop, let inter handle EOF endlop: popj p, ; Return to caller

    ; Close the work file
    clofil: move t1, jfn ; Fetch JFN from earlier
    closf% ; Should also release JFN
    erjmp errorf ; Handle error (no return)
    popj p, ; Doesn't actually return
    ; on error, but does on success

    ; Interrupt handler for end-of-file channel condition. File should still
    ; be open at this point. Close it.
    subttl EOF interrupt handler
    eof: TMSG (Not found. EOF reached.)
    pushj p, clofil ; Call close file routine
    haltf% ; Exit to monitor

    ; Generate a "bell". Since there's no audible bell on this system,
    ; print "Bell!" instead.
    subttl Bell interrupt handler
    bell: TMSG (
    Bell!
    ) ; Visual bell
    SETIME (elptim) ; Reset timer for elptim MS
    debrk% ; Return from interrupt

    ; Borrow error handler from Gorin text. Does not return (exits).
    subttl File Error Handler
    errorf: TMSG (Error: )
    esout% ; Clear any type-ahead
    move t1, [.priou] ; Error to terminal
    hrloi t2, .FHSLF ; This fork, most recent err
    setz t3, ; No byte count limit
    erstr% ; Convert last error to str
    jfcl
    jfcl ; Two possible error returns
    haltf% ; Exit but no restart

    lit ; Expand literals
    end start ; Assembler is done

    The keyboard int program is much smaller. It prints a message until you
    hit a key, which is hooked into channel zero. You can hook ctrl keys
    A-Z, Esc, and some others like type-in and type-out.

    @exec chbu4

    PDP is alive!

    ...

    PDP is alive!

    PDP is alive!

    Got stop interrupt, exiting


    comment $
    Write a program that continuously writes a message to the screen. Set
    up a keyboard interrupt so that the program will stop whenever the
    user presses a key (.TICTI).
    $
    title chbu4
    search monsym, macsym
    lall
    STDAC. ; Label accums

    ; Channel table as per Monitor Ref Calls manual
    chntab: 3,,stop ; Ch 0, prio 3, stop routine
    block chntab+^D36-. ; Fill rest of 36 words

    ; Table for priority levels
    levtab: 0 ; No prio 1
    0 ; nor 2
    pc3 ; Priority 3
    pc3: 0 ; Storage for PC for prio 3

    subttl Main
    start: reset
    move t1, [.FHSLF] ; Fork, self. Load only once
    move t2, [xwd levtab, chntab] ; Set up tables for SIR%
    sir% ; Please SIR% give ints
    eir% ; Turn them on
    move t2, [1B0] ; Turn on bit zero, chan 0
    aic% ; Activate interrupt channel
    move t1, [.TICTI,,0] ; Type-in on channel 0
    ati% ; for terminal interrupts
    loop: TMSG (
    PDP is alive!
    ) ; Message to use
    jumpa loop ; Infinite loop without ints done: haltf% ; Exit to monitor
    jrst start ; Restart program

    ; Interrupt handler for channel 0, type-in keypress
    subttl Stop interrupt handler
    stop: TMSG <Got stop interrupt, exiting.> ; Using ",", must use <, >
    haltf%
    jrst start ; Restart even on int

    lit ; Debugger 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 jayjwa@jayjwa@atr2.ath.cx.invalid to alt.lang.asm on Wed Aug 26 15:20:01 2026
    From Newsgroup: alt.lang.asm

    chbu5.mac

    Final program, and I'm done with "Introduction To DECSYSTEM-20 Assembly Programming" by Stephen Longo. It took about 14 weeks, less if you
    consider my being out-of-town for two of those. The rest of the book is
    the appendix: charts, codes, and interfacing with Pascal and FORTRAN. I probably won't do those because I don't use Pascal much on TOPS-10/20
    and FORTRAN not at all. There's something about real numbers in Appendix
    F; if that requires writing programs I might post those.

    All in all I liked this book, even though it had a few minor bugs and
    the macros in the final chapter didn't assemble (for me) and some of the programs were nasty-hard, like the stack-as-a-queue one, the recursive factorial one, or this one, which hit 52 generations (because of debrk%
    to a different place in the program being needed).

    The last program is a timer on an interrupt handler tied to pressing any
    key on the keyboard. The comment block, as usual, describes it. The
    spinning wheel doesn't look right unless the program is running; you'll
    have to imagine it after "Press any key".

    @exec chbu5
    MACRO: chbu5
    LINK: Loading
    [LNKXCT CHBU5 execution]
    Press any key to begin TIME demo.
    |
    Elapsed time in milliseconds: 9251
    @ cont
    Press any key to begin TIME demo.
    |
    Elapsed time in milliseconds: 22094
    @ cont
    Press any key to begin TIME demo.
    -
    Elapsed time in milliseconds: 1649


    comment $
    Write a program that will display the duration of an event. The event will start when the user presses a key, which should cause a keyboard interrupt
    that accesses TIME% . Store the value that is returned in ac 1. Stop
    the event when the user presses any key. This difference in the values
    of TIME% is the number of milliseconds between keystrokes.
    $
    title chbu5
    search monsym, macsym
    lall ; Expand macros
    STDAC. ; Label accums in standard way

    define SPINR < ; Print a spinner
    movei t1, .CHCRT
    pbout%
    movei t1, "|"
    pbout%
    movei t1, .CHCRT
    pbout%
    movei t1, "/"
    pbout%
    movei t1, .CHCRT
    pbout%
    movei t1, "-"
    pbout%
    movei t1, .CHCRT
    pbout%
    movei t1, "\"
    pbout%
    >

    ; Channel table as per Monitor Ref Calls manual
    chntab: 3,,etimer ; Ch 0, prio 3, routine etimer
    block chntab+^D36-. ; Fill rest of 36 words

    ; Table for priority levels
    levtab: 0 ; No prio 1
    0 ; No prio 2
    pc3 ; Prio 3
    pc3: 0 ; PC save point for prio 3 strtim: 0 ; Start time of TIMER
    endtim: 0 ; End time

    subttl Main
    start: reset
    setzm strtim ; Zero any last run
    setzm endtim
    TMSG (Press any key to begin TIME demo.
    )
    move t1, [.FHSLF] ; Fork, self
    move t2, [xwd levtab, chntab] ; Set up tables for SIR%
    sir% ; SIR%, you're needed
    eir% ; Enable interrupts
    move t2, [1B0] ; Turn on chan 0
    aic% ; Activate interrupt chans
    move t1, [.TICTI,,0] ; Type-in on channel 0
    ati% ; Active it

    pbin% ; Pause to start demo

    loop: SPINR ; Prints a spinner to wait
    jumpa loop ; Inf loop if not for interp

    done: TMSG (
    Elapsed time in milliseconds: ) ; Print heading
    move t2, endtim ; Calculate run time as
    sub t2, strtim ; endtim - strtim = MS run
    move t1, [.priou] ; Ready NOUT%
    movei t3, 12 ; Decimal base
    nout%
    erjmp [hrroi t1, [asciz /Error on NOUT%. Exiting/]
    psout%
    haltf%]
    haltf% ; Exit to monitor
    jrst start ; Restart program?

    ; Elapsed time handler for channel 0, key-in. The first time this is
    ; called 'strtim' will be zero, indicating to save the begin time. When
    ; it's not zero, and called again, this indicates to save the end time.
    subttl Timer handler
    etimer: move t1, strtim ; Check if strtim is 0
    skipn t1
    jrst [time% ; Yes, get start time
    movem t1, strtim ; And stash for later
    jrst exthdl] ; Exit handler
    time% ; Second time here
    movem t1, endtim ; Get, save end time

    ; On the second time here, we need to change the place that we return to.
    ; This is done by changing the levtab pointer to where the return address
    ; is stored.
    movei t2, done ; Change where we debrk% to
    movem t2, @levtab+2 ; which would be pc3
    exthdl: debrk% ; Return from handler

    lit ; Debugger expands 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 Kragen Javier Sitaker@kragen@canonical.org to alt.lang.asm on Tue Sep 1 20:43:09 2026
    From Newsgroup: alt.lang.asm

    jayjwa <jayjwa@atr2.ath.cx.invalid> writes:
    The others use macros constructed from examples spread out over the 3 previous chapters. This wouldn't be the first time there were errors
    in the text, but these macros nest 3-4 deep and I can't debug them
    because I don't know what I'm doing anyway (having just started
    interrupt study).

    I wonder if MACRO-20 has options for debugging macro expansion? The GNU assembler has some options that produce relatively complete macro
    expansion traces which I've found useful in debugging my gas macros.

    Kragen
    --- Synchronet 3.22a-Linux NewsLink 1.2