-
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