Forth на PDP-8

Digital Equipment Corporation PDP-8 & PDP-11 (а также совместимые с последним советские ЭВМ на 1801ВМ1/2/3)

Moderator: Shaos

AlexM1234
Junior
Posts: 4
Joined: 17 Dec 2025 12:09

Forth на PDP-8

Post by AlexM1234 »

Как бы продолжение этого топика: viewtopic.php?t=9038
Lavr wrote: 17 May 2020 22:07 P.S. И я тут разыскал исходник Форта для PDP-8 исторических времён, не новодел:

 FORTH COMPILER/INTERPRETER FOR TSS/8.24

Code: Select all

/ ** FORTH-8 **
/FORTH COMPILER/INTERPRETER
/FOR TSS/8.24 PAL/D assembler
/STARTED 29-JUL-84
/BY JOHN WILSON.
/
*0
  JMP START
/BETCHA DIDN'T KNOW YOU COULD JUMP-START A PROGRAM!
/
FIXTAB  BSW=  7002
/
JUMP=  JMS I .
  JUMP0  /JUMP TO ADDRESS FOLLOWING JMS
CALL=  JMS I .
  CALL0  /CALL TO ADDR FOLLOWING JMS, USING STACK
RET=  JMP I .
  RET0  /POP VALUE OFF STACK, JUMP TO IT
RPUSH=  JMS I .
  RPUSH0  /PUSH AC TO RETURN STACK
RPOP=  JMS I .
  RPOP0  /POP RETURN STACK TO AC
PPUSH=  JMS I .
  PPUSH0  /PUSH AC TO PARAMETER STACK
PPOP=  JMS I .
  PPOP0  /POP PARAMETER STACK TO AC
*10
INDEX1,  0  /SCRATCH AUTO-INDEXING POINTER
INDEX2,  0
INDEX3,  0
/CONSTANTS:
C0007,  7
C0027,  27
C0077,  77
C0240,  240
C7540,  7540
*20
DP,  USRDCT  /DICTIONARY PTR
IMMDP,  ICODE  /DICT PTR INTO IMMEDIATE BUFFER
/
SP,  0  /RETURN STACK POINTER
PSP,  0  /PARAMETER STACK POINTER
NSP,  0  /NESTING STACK POINTER
/
/TEMPORARY VARIABLES:
TEMP1,  0
TEMP2,  0
TEMP3,  0
TEMP4,  0
TEMP5,  0
/
ERROR=  JMS I .
  ERROR0  /PRINT TRIMMED ASCII ERROR MESSAGE AFTER JMS, & ABORT
PPUSHC=  JMS I .
  PPC0  /PUSH CONSTANT AFTER JMS TO PARAMETER STACK
NPUSH=  JMS I .
  NPUSH0  /PUSH AC AND WORD AFTER JMS TO NESTING STACK
NPOP=  JMS I .
  NPOP0  /CHECK FOR MATCH WITH WORD AFTER JMS, POP AC FROM NSTACK
DCTVAL=  JMS I .
  DCTVA0  /PUT AC IN DICT (ONLY), AND ADVANCE DP
DCTCON=  JMS I .
  DCTCO0  /PUT WORD FOLLOWING JMS IN DICT, ADVANCE DP
PUTVAL=  JMS I .
  PUTVA0  /PUT AC IN CURRENT BUFFER, AND ADVANCE PTR
PUTCON=  JMS I .
  PUTCO0  /PUT WORD FOLLOWING JMS IN CURR BUFF, & ADVANCE PTR
TYPE=  JMS I .
  TYPE0  /TYPE .ASCIZ STRING FOLLOWING JMS
PRINT=  JMS I .
  PRINT0  /PRINT CHAR IN AC, UPDATE PRCOL
CRLF=  JMS I .
  CRLF0  /PRINT CR, LF
RTS=  JMP I .
  DOWORD  /DO NEXT WORD (RETURN FROM TSTRU'S)
RUNIMM=  JMS I .
  RUNIM0  /RUN IMMEDIATE CODE, IF ANY
CURRDP=  JMS I .
  CURRD0  /RETURN DP FOR CURRENT BUFFER (SELON INWORD)
IF=  JMS I .
WHILE=  JMS I .
UNTIL=  JMS I .
  JUMPZ  /PPOP, JUMP TO @PC IF ZERO
DO=  JMS I .
  DO0  /COPY TOP TWO ARGS ON PSTACK TO RSTACK
LOOP=  JMS I .
  LOOP0  /INC @RSP, LOOP IF NOT EQUAL TO @RSP+1, ELSE CLEAR RSTACK
/
/NESTING STACK TOKENS:
TIF=  1  /IF-ELSE-THEN
TBEGIN=  2  /BEGIN-UNTIL, BEGIN-WHILE-REPEAT, BEGIN-AGAIN
TWHILE=  3  /BEGIN-WHILE-REPEAT
TDO=  4  /DO-LOOP, DO-+LOOP, DO-/LOOP
/
PRCOL,  0  /CURRENT PRINTING COLUMN (FOR TABS)
KBFILE,  0  /<>0 -> REDIRECTING KB INPUT FROM CHANNEL 0 (LOAD)
INWORD,  0  /<>0 -> COMPILING A WORD (: ... ;)
NLEVEL,  0  /CURRENT NESTING LEVEL
CARETF,  0  /<>0 -> PRINT "^" UNDER LAST WORD READ ON ERROR
ERRCOL,  0  /ADDR OF BEGN OF LAST WORD READ FROM LINE (FOR "^" ON ERR)
PROMPT,  0  /<>0 -> PROMPT WITH "OK" (ONLY IF THINGS ARE OKAY!)
DELFLG,  0  /<>0 -> BETWEEN \'S (ON INPUT)
LINPTR,  LINE  /PTR TO NEXT WORD IN LINE
/
M0211,  -211
WORDM1,  WORD-1
CLINE,  LINE
CWORD,  WORD
LOOKUP,  LOOKU0  /PTR TO LOOKUP ROUTINE
MAKEW,  MAKEW0  /PTR TO ROUTINE TO BUILD A WORD HEADER
GETWRD,  GETW  /PTR TO ROUTINE TO READ A WORD FROM THE KB
SMUDGE,  0  /PTR TO LINK TO LAST CREATED WORD (ZEROED ON ERR)
/
/INITIALIZE POINTERS, SET ^C VECTOR
  CTRLC  /^C VECTOR
START,  CLA+400    /=7600 (RSTACK/PSTACK BOUNDARY)
  TAD .-2    /GET ^C VECTOR
  SRA    /SET IT
  UND    /TURN OFF ECHO
  STA    /SET KB BREAK
  KSB    /TO ANYTHING
  TYPE    /PRINT "FORTH-8 BY JOHN WILSON"
  "F;"O+40;"R+40;"T+40;"H+40;"-;"8;240;240 /KINKY LOWER CASE
  "B+40;"Y+40;240;"J;"O+40;"H+40;"N+40;240
  "W;"I+40;"L+40;"S+40;"O+40;"N+40
  215;212;0  /CR,LF,<NUL>
/
RESET,  TAD START  /LOAD 7600
  DCA PSP    /SET PSTACK PTR
CTRLC,  CLA    /JUMP HERE ON ^C
  DCA I LINPTR  /FORCE KB READ ON NEXT GETW
  TAD ICODE0  /GET PTR TO IMM CODE
  DCA IMMDP  /POKE IT TO PTR
  DCA NSP    /SET NSTACK PTR
  DCA SP    /SET RSTACK PTR
  IAC    /SET BIT FOR CHAN 0 (KB REDIRECTION)
  ISZ KBFILE  /REDIRECTING INPUT?
  SKP CLA    /NO, DON'T WASTE FIP'S TIME
  CLOS    /YES, CLOSE THE FILE
  DCA KBFILE  /ANYWAY, WE'RE NOT NOW
  TAD SMUDGE  /KILL LAST WORD?
  SNA CLA
  JMP .+5    /NO, CONTINUE
  TAD I SMUDGE  /YES, GET LINK TO CONDEMNED WORD
  DCA DP    /BACK UP DP
  DCA I SMUDGE  /CLEAR OUT THE LINK
  DCA SMUDGE  /DON'T DO THIS AGAIN
  DCA NLEVEL  /CLEAR NESTING LEVEL
  CMA    /PRINT "^" UNDER ERRORS
  DCA CARETF
  DCA INWORD  /NOT IN A WORD (: DEF'N)
/THAT SHOULD DO IT - NOW START THE INTERPRETER
  JMP I .+1
C0200,  DOWORD    /JUMP INTO LOOP
ICODE0,  ICODE  /BEGN OF IMMEDIATE CODE
/
PAGE
/
DOWORD,  /GET AND COMPILE A WORD
  JMS GETW  /GET A WORD
  JMS I LOOKUP  /LOOK UP THE WORD
  SZL    /SKIP IF FOUND
  JMP DOWOR1  /NOT FOUND, MAYBE IT'S A #
  TAD I TEMP1  /GET TYPE
  DCA TEMP2  /SAVE
  ISZ TEMP1  /INC PTR
  JMP I TEMP2  /TYPE IS ALSO VECTOR
TCONS,  /CONSTANT
  TAD I TEMP1  /GET VALUE
  DCA TEMP1  /SAVE AS ADDR (FAKE OUT TVARI)
TVARI,  /VARIABLE
  PUTCON    /COMPILE A PPUSHC
  PPUSHC    /THAT'S WHAT I SAID
  TAD TEMP1  /GET ADDR (VALUE IF TCONS)
  PUTVAL    /COMPILE IT IN-LINE
  JMP DOWORD  /LOOP
TSUBR,  /SUBROUTINE (: ... ;)
  PUTCON    /COMPILE A CALL
  CALL    /YEP
  TAD TEMP1  /GET ADDR
  PUTVAL    /LOOKS AN AWFUL LOT LIKE TVARI!
  JMP DOWORD  /LOOP
TSTRU,  /CONTROL STRUCTURE (DO-LOOP, IF-ELSE-THEN, ETC.)
  JMP I TEMP1  /JUMP TO COMPILATION ROUTINE
/CONTROL STRUC ROUTINES RETURN VIA "RTS" (JMP I (DOWORD))
DOWOR1,  /WORD NOT DEFINED, CHECK FOR NUMBER
  TAD CWORD  /POINT AT WORD
  DCA TEMP1  /SAVE PTR
  JMS NUM    /SEE IF NUMBER
  SNL    /NO, ERROR
  JMP DOWOR2  /YES, VALUE IS IN TEMP2, GO PPUSHC IT
  ERROR    /UNDEFINED WORD
  TEXT /WHAT?/  /I JUST LOVE VAGUE MESSAGES!
DOWOR2,  PUTCON    /PPUSHC
  PPUSHC    /...
  TAD TEMP2  /GET VAL BACK
  PUTVAL    /COMPILE IN-LINE
  JMP DOWORD  /DO ANOTHER WORD
/
NUM,  0  /CONVERT .ASCIZ NUMBER AT @TEMP1 TO BINARY
/RESULT IN TEMP2, LINK SET IF NOT VALID NUMBER.
  DCA TEMP2  /CLEAR ACCUMULATOR
  DCA TEMP3  /CLEAR NEGATIVE FLAG
  TAD I TEMP1  /GET FIRST CHAR
  TAD MMINUS  /"-" ?
  SZA CLA    /SKIP IF SO
  JMP NUM1  /NO
  ISZ TEMP3  /SET FLAG
  ISZ TEMP1  /INC PTR
NUM1,  TAD I TEMP1  /GET A CHAR
  SNA    /SKIP IF NOT DONE
  JMP NUM2  /DONE, WHEE!
  TAD NUM4  /MOVE TO RANGE [-10,-1]
  CLL
  TAD C0012  /FLIP LINK IF VALID DECIMAL
  SNL    /SKIP IF HOOPY
  JMP NUM3  /INVALID DIGIT - NOT A NUMBER
  DCA TEMP4  /SAVE
  TAD TEMP2  /GET OLD NUMBER
  CLL RTL    /*4
  TAD TEMP2  /+NUMBER
  CLL RAL    /*2 =(((N*4)+N)*2)=N*10.
  TAD TEMP4  /ADD IN NEW DIGIT
  DCA TEMP2  /REPLACE
  ISZ TEMP1  /INC PTR
  JMP NUM1  /LOOP
NUM2,  TAD TEMP3  /NEGATE?
  SNA CLA
  JMP .+4    /NO, SKIP
  TAD TEMP2  /GET NUMBER
  CIA    /NEGATE
  DCA TEMP2  /REPLACE
  CLL    /CLEAR LINK - VALID NUMBER
  JMP I NUM  /RETURN WITH VALUE IN TEMP2
NUM3,  STL    /NOT A NUMBER
  JMP I NUM  /RETURN
NUM4,  -12-"0
C0012,  12
MMINUS,  -"-
/
GETW,  0
/READ A WORD FROM @LINPTR TO FIRST CTRL CHAR OR SPACE
/ENTER WITH AC CLEAR
/RETURN WITH .ASCIZ STRING IN WORD
  TAD WORDM1  /PT AT WORD (AX)
  DCA INDEX1  /OUTPUT PTR
GETW1,  TAD LINPTR  /SET ERRCOL TO POINT HERE
  DCA ERRCOL  /IN CASE THIS IS A NASTY WORD
  TAD I LINPTR  /GET CHAR
  SNA    /END OF LINE?
  JMP I READLN  /YES, GET ANOTHER ONE
  TAD M0241  /SPACE OR CTRL CHAR?
  SMA
  JMP GETW2  /NO
  IAC    /SPACE?
  SZA
  TAD C0027  /TAB?
  SZA CLA    /YES, ONE OR THE OTHER
  JMP .+3    /NO, ILLEGAL CHAR
  ISZ LINPTR  /INC PTR (IGNORE)
  JMP GETW1  /LOOP
  ERROR    /ILLEGAL CHAR
  TEXT /ILL CHAR/
GETW2,  CLA
GETW3,  TAD I LINPTR  /GET CHAR
  TAD M0241  /CTRL CHAR OR SPACE?
  SPA CLA
  JMP GETW4  /YES, GO RETURN
  TAD I LINPTR  /NO, RESTORE CHAR
  TAD C7405  /CVT LOWER CASE TO [-26.,-1]
  CLL
  TAD C0032  /LOWER CASE?
  SZL CLA    /NO, SKIP
  TAD M0040  /YES, SUBTRACT 32.
  TAD I LINPTR  /GET CHAR BACK
  DCA I INDEX1  /SAVE
  ISZ LINPTR  /INC PTR
  JMP GETW3  /LOOP
GETW4,  DCA I INDEX1  /MARK END WITH <NUL>
  JMP I GETW  /RETURN
M0241,  -241  /SPACE, CTRL CHARS -> [-32.,-1]
C0032,  32  /[-26.,-1] -> [0,25.] (FLIPPING LINK)
C7405,  7405  /LOWER CASE -> [-26.,-1]
M0040,  -40  /LOWER CASE -> UPPER CASE
READLN,  READL0  /PROMPT, GET A LINE
/
PAGE
/
READL0,  /CALLS INPUT, AND RESETS LINPTR
  RUNIMM    /RUN IMMEDIATE CODE FIRST
  TAD PROMPT  /PROMPT?
  SNA CLA
  JMP READL1  /NO
  TYPE    /YES, PRINT PROMPT
  "O+40;"K+40;215;212;0  /"OK<CR><LF>" (LOWER CASE)
READL1,  CMA    /REMEMBER TO PROMPT NEXT TIME, ANYWAY
  DCA PROMPT
  TAD CLINE  /PT AT LINE BUFFER
  DCA TEMP1  /PUT IN TEMP1
  JMS INPUT  /READ A LINE
  TAD CLINE  /GET PTR TO LINE
  DCA LINPTR  /PT AT LINE
  JMP I .+1  /TRY AGAIN
  GETW+1
/
INPUT,  0  /READ 80 CHARS FROM TTY TO @TEMP1
/ENTER WITH AC CLEAR
/0 MARKS END OF INPUT STRING
  TAD TEMP1  /GET START OF BUFFER
  CIA    /NEGATE
  DCA TEMP2  /USED TO CHECK BEG OF BUFF ON RUBOUT
  TAD TEMP1  /GET PTR
  DCA INDEX2  /SAVE IT
  TAD TEMP1  /GET START OF BUFFER
  TAD D0080  /PT AT LAST NON-CR CHAR IN BUFFER
  CIA    /NEGATE
  DCA TEMP3  /USED TO CHECK END OF BUFF ON INSERT
  DCA DELFLG  /NOT BETWEEN \'S
INPUT1,  KRB    /GET A CHAR
  DCA TEMP4  /SAVE IT
  TAD NUMSPC  /-(NUMBER OF SPECIALLY TREATED CHARS)
  DCA TEMP5  /LOOP CTR
  TAD CIDISP  /PT AT IDISP (DISP. TABLE)
  DCA INDEX1  /SAVE
  TAD TEMP4  /GET CHAR BACK
INPUT2,  TAD I INDEX1  /IS THIS THE CHAR?
  SNA    /SKIP IF THIS ISN'T IT
  JMP INPUT5  /THIS IS IT, GO JUMP TO IT
  ISZ INDEX1  /SKIP OVER ADDR
  ISZ TEMP5  /DONE?
  JMP INPUT2  /NO, TRY NEXT VALUE
  CLA
INPUT3,  /INSERT AND ECHO CHAR
  JMS BACKSL  /PRINT \ AND CLEAR DELFLG IF DELFLG
  TAD TEMP1  /GET PTR
  TAD TEMP3  /OUT OF SPACE?
  SZA CLA    /SKIP IF SO
  JMP INPUT4  /OTHERWISE CONTINUE
  TAD C0007  /RING BELL
  PRINT
  JMP INPUT1  /AND IGNORE
INPUT4,  TAD TEMP4  /GET CHAR
  DCA I TEMP1  /PUT IN BUFFER
  ISZ TEMP1  /INC PTR
  JMS I ECHOA  /ECHO THE CHAR
  JMP INPUT1  /LOOP
INPUT5,  /JUMP TO DISPATCH TABLE ADDRESS
  CLA    /CLEAR AC
  TAD I INDEX1  /GET DISPATCH VALUE
  DCA BACKSL  /SAVE
  JMP I BACKSL  /JUMP TO IT
BACKSL,  0  /IF DELFLG.NE.0, PRINT \ AND CLEAR DELFLG
  TAD DELFLG  /INSIDE \'S?
  SNA CLA
  JMP I BACKSL  /NO, RETURN
  DCA DELFLG  /CLEAR FLAG
  TAD .+3    /LOAD "\"
  PRINT    /PRINT IT
  JMP I BACKSL  /AND RETURN
  "\    /DELIMITS DELETED CHAR'S
INPUT6,  /MARK END OF STRING AND RETURN
  JMS BACKSL  /"\" IF NESSA
  DCA I TEMP1  /ZERO AT END
  CRLF    /CR,LF
  JMP I INPUT  /RETURN
INPUT7,  /^R - CR,LF AND REDRAW STRING
  JMS BACKSL  /"\" IF NESSA
  JMS I ECHOA  /PRINT "^R"
  CRLF    /CR,LF
  DCA I TEMP1  /MARK END OF STRING, FOR NOW
  CMA    /LOAD -1
  TAD INDEX2  /PT AT START OF STRING -1
  DCA INDEX3  /SAVE
INPUT8,  TAD I INDEX3  /GET A CHAR
  SNA    /SKIP IF NOT ZERO
  JMP INPUT1  /GET ANOTHER CHAR
  DCA TEMP4  /SAVE THE CHAR
  JMS I ECHOA  /ECHO IT
  JMP INPUT8  /LOOP UNTIL 0
INPUT9,  /^U - CR,LF AND CANCEL STRING
  JMS I ECHOA  /PRINT "^U"
  CRLF    /CR,LF
  TAD INDEX2  /GET START OF BUFFER
  DCA TEMP1  /RESET POINTER
  JMP INPUT1  /GET ANOTHER CHAR
  "$
INPU10,  /ESCAPE - ECHO $ AND RETURN
  JMS BACKSL  /"\" IF NESSA
  TAD .-2    /GET "$"
  PRINT    /PRINT IT
  DCA I TEMP1  /MARK END OF STRING
  JMP I INPUT  /AND RETURN
/
RUBOUT,  /RUBOUT - PRINT "\" (IF NESSA) AND ECHO LAST CHAR
  TAD TEMP1  /GET PTR
  TAD TEMP2  /GET -(START)
  SZA CLA    /BUFFER EMPTY?
  JMP RUB1  /NO
  JMS BACKSL  /PRINT CLOSING \ IF NESSA
  CRLF    /YES, CR,LF
  JMP INPUT1  /AND IGNORE
RUB1,  TAD DELFLG  /NEED A "\" ?
  SZA CLA
  JMP RUB2  /NO, ALREADY INSIDE \'S
  TAD INPUT6-1  /GET \
  PRINT    /PRINT IT
  ISZ DELFLG  /SET DELFLG
RUB2,  CMA    /BACK UP PTR
  TAD TEMP1
  DCA TEMP1
  TAD I TEMP1  /GET CHAR TO BE DELETED
  DCA TEMP4  /PUT IN CHAR BUFF
  JMS I ECHOA  /ECHO IT
  TAD TEMP1  /AT BEGN OF BUFF NOW?
  TAD TEMP2
  SZA CLA
  JMP INPUT1  /NO, LOOP
  JMS BACKSL  /PRINT "\"
  CRLF    /CR,LF
  JMP INPUT1  /AND DO ANOTHER CHAR
ECHOA,  ECHO  /PTR TO ECHO ROUTINE
D0080,  120  /80. (LENGTH OF BUFFER)
NUMSPC,  -13  /-(NUMBER OF SPECIAL INPUT CHARS)
CIDISP,  IDISP-1  /PTR TO INPUT DISPATCH TABLE
/
PAGE
/
ECHO,  0  /ECHO CHAR IN TEMP4
  TAD TEMP4  /GET CHAR
  AND C7540  /CLEAR AC4,AC7-AC11
  SNA CLA    /CTRL CHAR?
  JMP ECHO2  /YES, SPECIAL TREATMENT
ECHO1,  TAD TEMP4  /GET CHAR
  PRINT    /PRINT IT
  JMP I ECHO  /RETURN
ECHO2,  TAD TEMP4  /GET CHAR
  TAD M0211  /TAB?
  SZA    /SKIP IF SO
  IAC    /BACKSPACE?
  SNA CLA    /SKIP IF NOT
  JMP ECHO1  /HT & BS ARE ECHOED AS-IS
  TAD UPARR  /PRINT "^"
  PRINT
  7203    /CLA IAC BSW (PAL-D BUG) LOAD 64.
  TAD TEMP4  /LOAD CHAR+64.
  PRINT    /ECHO IT
  JMP I ECHO  /RETURN
UPARR,  "^  /FOR CTRL CHARS
/
/DISPATCH TABLE FOR SPECIAL CHARACTERS:
IDISP,  /INPUT DISPATCH TABLE (RO,ESC,^U,^R,^M,^L,^K,^J,^I,^H,NUL)
  -377  /RUBOUT
  RUBOUT
  377-233  /ESCAPE
  INPU10
  233-225  /^U
  INPUT9
  225-222  /^R
  INPUT7
  222-215  /CR
  INPUT6
  215-214  /FF
  INPUT6
  214-213  /VT
  INPUT6
  213-212  /LF
  INPUT6
  212-211  /TAB
  INPUT3
  211-210  /BACKSPACE
  INPUT3
  210-200  /NUL
  INPUT1
/DWEE!
/
  0  /TEMP BUFFER FOR CHAR
PRINT0,  0  /PRINT CHAR IN AC, DEAL WITH TABS
  DCA PRINT0-1  /SAVE
  TAD PRINT0-1  /GET BACK
  TAD M0211  /TAB?
  SNA
  JMP TAB    /YES, DEAL WITH IT
  IAC    /BACKSPACE?
  SNA    /NO, SKIP
  JMP BCKSPC  /YES, SPECIAL TREATMENT
  TAD C7773  /CR?
  SNA CLA
  JMP CARRET  /YES
  TAD PRINT0-1  /GET CHAR
  AND C7540  /CTRL CHAR?
  SZA CLA    /SKIP IF SO
  ISZ PRCOL  /OTHERWISE INC PRCOL
PRINT1,  TAD PRINT0-1  /GET CHAR
  TLS    /PRINT IT
  CLA    /CLEAR AC
  JMP I PRINT0  /RETURN
/
BCKSPC,  CLL CMA    /DEC PRCOL
  TAD PRCOL
  SZL    /UNLESS IT'S ALREADY 0 (DEC TERMINALS)
  DCA PRCOL
  CLA    /IN CASE WE JUST SKIPPED
  JMP PRINT1  /PRINT BS AND RETURN
TAB,  TAD C0240  /PRINT A SPACE
  TLS
  CLA
  ISZ PRCOL  /MOVE OVER A COL
  TAD PRCOL  /GET PRCOL
  AND C0007  /AT A TAB STOP?
  SZA CLA    /SKIP IF SO
  JMP TAB    /ELSE LOOP
  JMP I PRINT0  /RETURN
CARRET,  DCA PRCOL  /CLEAR PRINT COLUMN
  JMP PRINT1  /PRINT CR AND RETURN
/
C7773,  7773
/
CRLF0,  0  /PRINT CR,LF
  TYPE    /A LITTLE SLOW, BUT SO WHAT?
  215;212;0  /CR,LF
  JMP I CRLF0  /RETURN
/
TYPE0,  0  /PRINT .ASCIZ STRING FOLLOWING "TYPE"
  TAD I TYPE0  /GET CHAR
  ISZ TYPE0  /INC PTR
  SNA    /DONE YET?
  JMP I TYPE0  /YES, RETURN
  PRINT    /NO, PRINT THE CHAR
  JMP TYPE0+1  /LOOP UNTIL 0
/
RPUSH0,  0  /PUSH AC TO RETURN STACK (7600-7777)
  DCA TYPE0  /SAVE AC
  TAD SP    /GET SP
  TAD C0200  /STACK FULL?
  SZA CLA
  JMP RPUSH1  /NO, GO PUSH
  ERROR    /YES, PRINT MESSAGE
  TEXT /RSTACK OVERFLOW/
RPUSH1,  CMA    /DEC SP
  TAD SP
  DCA SP
  TAD TYPE0  /GET AC BACK
  DCA I SP  /PUSH IT
  JMP I RPUSH0  /RETURN
/
PAGE
/
ERROR0,  0  /PRINT TRIMMED ASCII MESSAGE AFTER JMS & ABORT
  TAD CARETF  /PRINT "^"?
  SNA CLA
  JMP ERROR3  /NO
  TAD ERRCOL  /GET COLUMN OF ERROR
  CIA    /NEGATE
  DCA TEMP1  /SAVE
  TAD CLINE  /PT AT LINE
  DCA TEMP2  /SAVE
ERROR1,  TAD TEMP1  /DONE?
  TAD TEMP2
  SNA CLA
  JMP ERROR2  /YES, GO PRINT "^ "
  TAD I TEMP2  /GET CHAR
  TAD M0211  /TAB?
  SZA CLA
  TAD C0027  /NO, PRINT A SPACE
  TAD C0211  /YES, PRINT A TAB
  PRINT    /HACK, HACK, HACK
  ISZ TEMP2  /INC PTR
  JMP ERROR1  /LOOP
ERROR2,  TYPE    /PRINT "^ "
  "^;" ;0
ERROR3,  STA CLL RAL  /LOAD -2
  DCA RPOP0  /SAVE LOOP CTR
  TAD I ERROR0  /GET CHAR
  BSW    /GET HIGH CHAR
ERROR4,  AND C0077  /MASK OUT HIGH BITS
  SNA    /DONE?
  JMP ERROR6  /YES, CR/LF AND RESTART
  DCA LOOKU0  /NO, SAVE THE CHAR
  TAD LOOKU0  /GET BACK
  CMA    /FLIP EVERYTHING
  AND C0040  /BIT 6 SET?
  CLL RAL    /HACK HACK HACK
  TAD C0200  /ADD MISSING BITS
  TAD LOOKU0  /GET CHAR BACK
  PRINT    /PRINT IT
  ISZ RPOP0  /WAS THAT THE HIGH OR THE LOW CHAR?
  JMP ERROR5  /HIGH
  ISZ ERROR0  /LOW - PT TO NEXT CHAR
  JMP ERROR3  /DO THE NEXT TWO CHARS
ERROR5,  TAD I ERROR0  /GET LOW CHAR
  JMP ERROR4  /PRINT IT
ERROR6,  CRLF    /CR,LF
  DCA PROMPT  /NOT "OK"
  JMP RESET  /GO RESTART
C0040,  40
C0211,  211  /TAB
/
LOOKU0,  0  /LOOK UP WORD IN WORD BUFFER
/RETURN WITH LINK CLEAR AND TEMP1 POINTING AT TYPE IF
/FOUND, OR WITH LINK SET IF NOT FOUND.
  TAD CDICT  /PT AT DICT
  DCA TEMP1
  DCA TEMP2  /PTR TO LAST LINK (FORGET)
LOOKU1,  TAD WORDM1  /#WORD -1
  DCA INDEX1  /SOURCE PTR (AX)
LOOKU2,  CMA CLL RAL  /LOAD -2 (2 CH'S/WORD)
  DCA TEMP3  /LOOP CTR
  TAD I TEMP1  /GET 2 CHARS
  BSW    /LOOK AT HIGH ORDER FIRST
LOOKU3,  AND C0077  /REMOVE HIGH CHAR
  SNA    /END OF ENTRY?
  JMP LOOKU5  /YES, SEE IF END OF WORD
  TAD C0240  /CVT TO ASCII
  CIA    /NEGATE
  TAD I INDEX1  /=?
  SZA CLA
  JMP LOOKU6  /NO, SKIP TO LINK
  ISZ TEMP3  /FINISHED WORD?
  JMP LOOKU4  /NO, CHECK LOW CHAR
  ISZ TEMP1  /YES, PT AT NEXT PAIR OF CHARS
  JMP LOOKU2  /LOOP
LOOKU4,  TAD I TEMP1  /GET PAIR OF CHARS BACK
  JMP LOOKU3  /CHECK LOW CHAR
LOOKU5,  TAD I INDEX1  /END OF WORD?
  SZA CLA
  JMP LOOKU6  /NO, SKIP TO NEXT ENTRY
  ISZ TEMP1  /YES, PT AT LINK
  ISZ TEMP1  /PT AT TYPE
  CLL    /SIGNIFY FOUND
  JMP I LOOKU0  /RETURN
LOOKU6,  TAD I TEMP1  /GET CHAR
  ISZ TEMP1  /PT AT NEXT CHAR OR LINK
  AND C0077  /BOTTOM BYTE SHOULD BE 0, ANYWAY
  SZA CLA    /IS IT?
  JMP LOOKU6  /NO, TRY NEXT WORD
  TAD TEMP1  /YES, GET PTR TO LINK
  DCA TEMP2  /SAVE IN CASE OF <FORGET>
  TAD I TEMP1  /GET LINK
  STL    /IN CASE THIS IS THE END OF THE DICT
  SNA    /IS IT?
  JMP I LOOKU0  /YES, RETURN, LINK SET
  DCA TEMP1  /NO, PT TO NEXT NAME FIELD
  JMP LOOKU1  /CHECK THIS ENTRY
CDICT,  DICT  /BEGINNING OF DICTIONARY
/
DCTVA0,  0  /PUT AC IN DICTIONARY, ADVANCE DP (CHECK PSTACK)
  DCA I DP  /THERE IS ALWAYS AT LEAST 1 FREE WORD
  TAD DP    /GET DP
  CMA    /NEGATE, SUBTRACT 1 (HACK HACK)
  TAD PSP    /PSTACK COLLISION?
  SZA CLA
  JMP DCTVA1  /NO
  ERROR    /YES, ERROR
  TEXT /DICT FULL/
DCTVA1,  ISZ DP    /ALL OK, INC DP
  JMP I DCTVA0  /RETURN, AC CLEAR
/
DCTCO0,  0  /PUT WORD AFTER JMS IN DICT, ADVANCE DP
  CLA
  TAD I DCTCO0  /GET WORD TO PUT
  JMS DCTVA0  /PUT IT IN THE DICT
  ISZ DCTCO0  /PT PAST ARG
  JMP I DCTCO0  /RETURN, AC CLEAR
/
PAGE
/
MAKEW0,  0  /CREATE ENTRY FOR WORD IN WORD BUF IN DICT
/FIRST, CHECK TO MAKE SURE THE WORD DOESN'T ALREADY EXIST.
/USE TYPE IN WORD FOLLOWING JMS.
  JMS I LOOKUP  /TRY TO LOOK THE WORD UP
  SZL    /SKIP IF FOUND
  JMP MAKEW1  /NOT THERE, MAKE IT
  ERROR    /ALREADY DEFINED
  TEXT /ILL REDEF'N/
MAKEW1,  TAD TEMP2  /GET PTR TO LINK IN LAST ENTRY
  DCA SMUDGE  /SAVE IN CASE OF COMPILATION ERROR
  TAD WORDM1  /PT AT WORD
  DCA INDEX1  /SOURCE PTR
MAKEW2,  CMA CLL RAL  /-2
  DCA TEMP1  /LOOP CTR
MAKEW3,  TAD I INDEX1  /GET A CHAR
  SNA    /END?
  JMP MAKEW6  /YES, FINISH OFF
  TAD M0240  /NO, CVT TO SIXBIT
  DCA TEMP2  /SAVE
  TAD TEMP2  /CHECK
  AND C7700  /ANY EXTRA BITS?
  SNA CLA
  JMP MAKEW4  /OK, CONTINUE
  ERROR    /ILLEGAL NAME
  TEXT /ILL NAME/
MAKEW4,  TAD TEMP2  /GET CHAR
  ISZ TEMP1  /CHAR 1, OR 2?
  JMP MAKEW5  /1
  TAD TEMP3  /2 - GET OLD CHAR
  DCTVAL    /COMPILE INTO DICTIONARY
  JMP MAKEW2  /LOOP
MAKEW5,  BSW    /LEFT 6 BITS
  DCA TEMP3  /SAVE
  JMP MAKEW3  /LOOP
MAKEW6,  /END OF WORD
  ISZ TEMP1  /CHAR 1, OR 2?
  SKP    /1 - PUT 0,0
  TAD TEMP3  /2 - PUT CHAR,0
  DCTVAL    /COMPILE LAST WORD OF NAME
  DCTVAL    /LINK IS 0
  TAD I MAKEW0  /GET TYPE
  DCTVAL    /COMPILE IT
  ISZ MAKEW0  /SKIP OVER TYPE
  JMP I MAKEW0  /RETURN
C7700,  7700  /CLEAR OUT CHAR
M0240,  -240  /CONVERT ASCII TO SIXBIT
/
PUTVA0,  0  /PUT AC IN DICT OR IMMBUF, DEPENDING ON INWORD
  DCA MAKEW0  /SAVE AC
  TAD INWORD  /IN A : DEFN?
  SNA CLA    /SKIP IF SO
  JMP PUTVA1  /ELSEWISE, GO PUT IN ICODE
  TAD MAKEW0  /GET AC
  DCTVAL    /SAVE IN DICT
  JMP I PUTVA0  /RETURN
PUTVA1,  TAD IMMDP  /GET IMMEDIATE PSEUDO-DICTIONARY PTR
  TAD PUTVA3  /ICODE FULL?
  SZA CLA    /YES, ERROR
  JMP PUTVA2  /NO, POKE THE VALUE
  ERROR
  TEXT /IMM BUFF FULL/
PUTVA2,  TAD MAKEW0  /GET AC
  DCA I IMMDP  /PUT IN ICODE
  ISZ IMMDP  /INC PTR
  JMP I PUTVA0  /RETURN
PUTVA3,  -ICODE-176  /ALWAYS LEAVE ONE EXTRA WORD FOR "RET"
/
PUTCO0,  0  /PUT WORD AFTER JMS IN APPROPRIATE BUFFER
  CLA
  TAD I PUTCO0  /GET WORD
  JMS PUTVA0  /POKE TO BUFFER
  ISZ PUTCO0  /INC PTR
  JMP I PUTCO0  /RETURN
/
JUMP0,  0  /JUMP TO ADDR AFTER JMS
  CLA
  TAD I JUMP0  /GET ADDR
  DCA JUMP0  /SAVE
  JMP I JUMP0  /JUMP TO IT
/
CALL0,  0  /CALL ADDR FOLLOWING "CALL," USING RSTACK
  CLA IAC    /LOAD 1
  TAD CALL0  /RETURN ADDRESS
  RPUSH    /PUSH TO RSTACK
  TAD I CALL0  /GET ADDR TO CALL
  DCA CALL0  /SAVE
  JMP I CALL0  /JUMP TO IT
/
RET0,  /POP PC OFF OF RSTACK
  RPOP    /GET RETURN ADDRESS
  DCA CALL0  /SAVE
  JMP I CALL0  /JUMP TO IT
/
PPUSH0,  0  /PUSH AC TO PSTACK
  DCA CALL0  /SAVE AC
  CMA    /DEC PSP
  TAD PSP
  DCA PSP
  TAD DP    /PSP-DP-1=0?
  CMA
  TAD PSP
  SZA CLA
  JMP PPUSH1  /NO, EVERYTHING'S HIP
  ERROR
  TEXT /PSTACK OVERFLOW/
PPUSH1,  TAD CALL0  /RESTORE AC
  DCA I PSP  /PUSH IT
  JMP I PPUSH0
/
PAGE
/
PPOP0,  0  /POP AC FROM PSTACK
  CLA
  TAD PSP    /GET PSP
  TAD C0200  /PSP=7600?
  SZA CLA
  JMP PPOP1  /NO
  ERROR
  TEXT /PSTACK UNDFL/
PPOP1,  TAD I PSP  /POP A VALUE
  ISZ PSP    /INC PSP
  JMP I PPOP0  /RETURN
/
RPOP0,  0  /POP AC FROM RETURN STACK (7600-7777)
  TAD SP    /GET SP
  SZA CLA
  JMP RPOP1  /STACK OK, GO POP
  ERROR    /UNDERFLOW, PRINT ERROR
  TEXT /RSTACK UNDFL/
RPOP1,  TAD I SP  /GET AC
  ISZ SP    /INC SP
  NOP    /THIS WILL SKIP ON THE LAST WORD
  JMP I RPOP0  /RETURN
/
PPC0,  0  /PPUSH CONSTANT IN LOCN AFTER "PPUSHC"
  CLA
  TAD I PPC0  /GET CONSTANT
  PPUSH    /PPUSH IT
  ISZ PPC0  /INC PAST ARG
  JMP I PPC0  /RETURN
/
RUNIM0,  0  /RUN IMMEDIATE CODE
  TAD INWORD  /COMPILING A : DEFN?
  SZA CLA
  JMP IMM1  /YES, DON'T WORRY ABOUT NESTING
  TAD NLEVEL  /NLEVEL=NSP?
  CIA
  TAD NSP
  SNA CLA
  JMP IMM1  /YES, NO PROBLEM
  ERROR    /NO, GACK
  TEXT /UNCLOSED STRUC/
IMM1,  DCA CARETF  /NO "^" ON RUN-TIME ERRORS
  TAD CRET  /"RET" AT END OF IMM CODE
  DCA I IMMDP
  CALL    /CALL THE CODE
  ICODE
  TAD CICODE  /PT AT ICODE
  DCA IMMDP  /RESET IMMDP
  CMA    /^ ON ERRORS
  DCA CARETF
  JMP I RUNIM0  /RETURN
CRET,  RET
CICODE,  ICODE
/
NPUSH0,  0  /PUSH AC, @PC TO NESTING STACK
  DCA RPOP0  /SAVE AC
  TAD NSP    /GET NSTACK PTR
  TAD NPUSH2  /STACK FULL?
  SZA CLA
  JMP NPUSH1  /NO
  ERROR    /YES, ERROR
  TEXT /TOO MUCH NESTING/
NPUSH1,  TAD NSP    /GET OFFSET
  TAD CNSTAK  /ADD BASE
  DCA PPC0  /SAVE PTR
  TAD RPOP0  /GET ADDRESS BACK
  DCA I PPC0  /PUSH IT
  ISZ PPC0  /INC PTR
  ISZ NSP    /INC OFFSET
  TAD I NPUSH0  /GET KEYWORD TOKEN
  DCA I PPC0  /PUSH IT
  ISZ NSP    /INC OFFSET (NOT PTR)
  ISZ NPUSH0  /SKIP OVER ARG
  JMP I NPUSH0  /RETURN
NPUSH2,  -40  /STACK BIG ENOUGH FOR 16 LEVELS OF NESTING
CNSTAK,  NSTACK  /PTR TO BASE OF NSTACK
/
PAGE
/
NPOP0,  0  /POP AC OFF OF NESTING STACK
/CHECK TO SEE IF TOKEN AGREES WITH ONE AFTER CALL (NEGATIVE)
  CLA    /JUST TO MAKE SURE
  TAD NSP    /GET NSTACK PTR
  SZA    /SKIP IF EMPTY
  JMP NPOP1  /OTHERWISE START THE POP
  ERROR    /NO MATCHING NPUSH
  TEXT /STRUC ERR/
NPOP1,  TAD NSTAK1  /ADD BASE TO OFFSET
  DCA TEMP5  /SAVE THIS PTR
  TAD I TEMP5  /GET TOKEN
  TAD I NPOP0  /GET -TOKEN TO MATCH
  SNA CLA    /DO THEY MATCH?
  JMP NPOP2  /YES
  ERROR    /NO
  TEXT /NEST ERR/
NPOP2,  STA CLL RAL  /-2
  TAD NSP    /DEC NSP BY 2
  DCA NSP
  STA    /DEC PTR BY 1
  TAD TEMP5
  DCA TEMP5
  TAD I TEMP5  /GET ADDRESS
  ISZ NPOP0  /SKIP OVER TOKEN
  JMP I NPOP0  /RETURN
NSTAK1,  NSTACK-1
/
CURRD0,  0  /RETURN DP FOR CURRENT BUFFER
  CLA    /JUST MAKING SURE
  TAD INWORD  /IN A COLON DEFINITION?
  SZA CLA
  JMP .+3    /YES
  TAD IMMDP  /NO
  JMP I CURRD0  /RETURN
  TAD DP    /GET DICTIONARY POINTER
  JMP I CURRD0  /RETURN
/
/DATA AREAS
NSTACK=  .  /NESTING STACK
*.+40    /ALLOW 16. LEVELS OF NESTING
WORD=  .  /BUFFER FOR WORD
*.+40    /ALLOW 32. CHARS BEFORE HITTING LINE
LINE=  .  /BUFFER FOR INPUT LINE
*.+120    /ALLOW 80 CHARS
ICODE=  .  /BUFFER FOR IMMEDIATE MODE CODE
*.+200    /ALLOW A LOT OF THAT
DICT=  .  /START OF PREDEFINED DICTIONARY
/
  0100  /!
  PAREN
  TSUBR
/( VAL ADDR -- ) POKES ADDR TO VAL
  PPOP    /GET ADDRESS
  DCA TEMP1  /SAVE
  PPOP    /GET VALUE
  DCA I TEMP1  /POKE
  RET    /RETURN
/
PAREN,  1000  /(
  MULT
  TSTRU
/( -- TEXT ')') IGNORES TEXT TO NEXT ")"
  CLA
PAREN1,  TAD I LINPTR  /LOOK FOR ")"
  ISZ LINPTR  /PT AT NEXT CHAR
  SNA    /END OF LINE?
  JMP PAREN2  /YES, GO READ ANOTHER ONE (BIG COMMENT)
  TAD PAREN3  /")" ?
  SZA CLA
  JMP PAREN1  /NO, LOOP
  RTS    /YES, RETURN
PAREN2,  TAD CLINE  /PT AT LINE BUFF
  DCA TEMP1  /SAVE PTR FOR INPUT
  JMS I PAREN4  /CALL INPUT
  TAD CLINE  /PT AT LINE, AGAIN
  DCA LINPTR  /SET LINPTR TO POINT THERE
  JMP PAREN1  /SEARCH LINE FOR ")"
PAREN3,  -")  /SEARCH CHARACTER
PAREN4,  INPUT
/
MULT,  1200  /*
  PLUS
  TSUBR
  PPOP    /GET FIRST VALUE
  DCA TEMP1  /SAVE IT
  PPOP    /GET SECOND VALUE
  DCA TEMP2  /SAVE IT
  JMS MULT0  /PERFORM THE MULTIPLY
  TAD TEMP4  /GET THE PRODUCT
  PPUSH    /RETURN IT
  RET
/
MULT0,  0  /MULTIPLY TEMP1 BY TEMP2, RESULT IN TEMP4
  TAD M0014  /-12.
  DCA TEMP3  /BIT COUNTER
  DCA TEMP4  /ACCUMULATOR
MULT1,  TAD TEMP1  /GET MULTIPLIER
  RAR    /AC11 INTO LINK
  DCA TEMP1  /SAVE RESULT
  SNL    /SKIP IF 1
  JMP MULT2  /0, DON'T ADD
  TAD TEMP4  /GET ACCUMULATOR
  TAD TEMP2  /ADD MULTIPLICAND
  DCA TEMP4  /SAVE SUM
MULT2,  TAD TEMP2  /ROTATE MULTIPLICAND LEFT ONE BIT
  CLL RAL
  DCA TEMP2
  ISZ TEMP3  /DONE ALL 12. BITS?
  JMP MULT1  /NO, LOOP
  JMP I MULT0  /YES, RETURN (RESULT IN TEMP4)
M0014,  -14  /-(NUMBER OF BITS IN A WORD)
/
PLUS,  1300  /+
  COMMA
  TSUBR
( VAL1 VAL2 -- VAL1+VAL2) RETURNS SUM OF TOP TWO VALUES
  PPOP    /GET FIRST VALUE
  DCA TEMP1  /SAVE IT
  PPOP    /GET SECOND VALUE
  TAD TEMP1  /ADD FIRST VALUE
  PPUSH    /PUSH SUM
  RET
/
COMMA,  1400  /,
  MINUS
  TSUBR
( VALUE -- ) PUTS VALUE INTO DICTIONARY AND ADVANCES DP
  PPOP    /GET VALUE
  DCTVAL    /POKE INTO DICTIONARY
  RET
/
MINUS,  1500  /-
  DOT
  TSUBR
( VAL1 VAL2 -- VAL1-VAL2) RETURNS DIFFERENCE BETWIXT TOP TWO VALUES
  PPOP    /GET VAL1
  DCA TEMP1  /SAVE
  PPOP    /GET VAL2
  CIA    /NEGATE
  TAD TEMP1  /ADD VAL1
  PPUSH    /PUSH RESULT
  RET
/
DOT,  1600  /.
  DOTQ
  TSUBR
( VAL -- ) PRINTS VALUE ON TOP OF STACK, SIGNED
  PPOP    /GET VALUE
  JMS .+2    /PRINT THE VALUE
  RET    /RETURN
/
PRNUM0,  0  /PRINT SPACE, AC (SIGNED)
  SMA    /NEGATIVE?
  JMP PRNUM1  /NO
  CIA    /MAKE POSITIVE
  DCA TEMP1  /SAVE
  TYPE    /PRINT " -"
  " ;"-;0
  JMP PRNUM2  /GO PRINT THE #
PRNUM1,  DCA TEMP1  /SAVE NUMBER
  TYPE    /PRINT " "
  " ;0
PRNUM2,  TAD TEMP1  /GET NUMBER BACK
  JMS I PRNUM3  /PRINT IT
  RET    /AND RETURN
PRNUM3,  UPRNUM  /ROUTINE TO PRINT A #, UNSIGNED
/
*.  /TO AVOID "PE" ERRORS (PAL-D BUG)
DOTQ,  1602  /."
  0000
  DIVD
  TSTRU    /SPECIAL CASE COMPILATION
  PUTCON    /COMPILE "DOTQUO"
  DOTQUO
  JMS ASCIZ0  /COMPILE IN-LINE .ASCIZ CONSTANT
  RTS
/
ASCIZ0,  0  /COMPILE IN-LINE STRING TERMINATED WITH "
  TAD I LINPTR  /END OF LINE HERE?
  SNA CLA    /NO
  JMP ASCIZE  /YES, ERROR (UNTERMINATED)
ASCIZ1,  ISZ LINPTR  /PT AT NEXT CHAR
  TAD I LINPTR  /GET A CHAR (LOWER CASE OK)
  SNA    /SHOULDN'T BE END OF LINE
  JMP ASCIZE  /ERROR IF SO
  TAD ASCIZ3  /END OF STRING?
  SNA CLA
  JMP ASCIZ2  /YES, <NUL> AT END AND RETURN
  TAD I LINPTR  /NO, RESTORE CHAR
  PUTVAL    /PUT IN DICTIONARY
  JMP ASCIZ1  /LOOP
ASCIZ2,  ISZ LINPTR  /SKIP OVER "
  PUTVAL    /COMPILE A <NUL>
  RTS    /AND RETURN
ASCIZ3,  -242  /QUOTE
ASCIZE,  ERROR  /ERROR IF END OF LINE ENCOUNTERED
  TEXT /NO CLOSING "/
/
DOTQU0,  0  /RUN-TIME ROUTINE TO PRINT .ASCIZ STRING FOLLOWING CALL
  CLA    /JUST TO MAKE SURE
DOTQU1,  TAD I DOTQU0  /GET A CHAR
  ISZ DOTQU0  /PT AT NEXT ONE
  SNA    /DONE?
  JMP I DOTQU0  /YES, RETURN
  PRINT    /NO, PRINT THE CHAR
  JMP DOTQU1  /LOOP UNTIL 0
/
DIVD,  1700  /"/"
/ADD THIS LATER
USRDCT=  .  /USER-DEFINED WORDS START HERE
$ THAT'S ALL, FOLKS!

b2m wrote: 23 May 2020 05:22
Lavr wrote:А там где есть и аппаратный стек, и развитая косвенная адресация (как в z80) - там удивляться
особо нечему...
Между прочим, наличие у PDP-8 автоинкрементной индексации позволяет сделать форт с прямым шитым кодом, который будет гораздо эффективнее того, что реализовано Джоном.

Здесь мы имеем такой код (предположим скомпилированы слова WORD1 WORD2):

Code: Select all

JMS I [CALL0]
WORD1
JMS I [CALL0]
WORD2
В конце каждого слова идет вызов JMS I [RET0], исходный код этих процедур я приводить не буду, но действий там не мало.

А теперь рассмотрим гипотетический прямой шитый код:

Code: Select all

WORD1
WORD2
Предположим, что текущий указатель на форт-программу у нас в INDEX1. Тогда адресный интерпретатор сократится лишь до 4-х команд:

Code: Select all

CLA
TAD I INDEX1
DCA TEMP1
JMP I TEMP1
Эти команды нужно ставить в конце ассемблерных слов вместо RET, ну или оформить в виде процедуры NEXT.
Конечно, как и в любом форте с прямым шитым кодом, форт-слово будет начинаться вызовом JMS I [CALL], а завершаться словом EXIT. Но учитывая, что в конечном счёте чаще всего будут вызываться ассемблерные слова, в которых этих вызовов конечно нет, то исполнение будет гораздо быстрее. И это не учитывая того, что прямой шитый код в два раза компактнее.
Та же самая история - периодически возникает интерес к Форту с точки зрения его реализации, но интерес быстро пропадает при попытках что-то писать на самом Форте :esmile:

Я как раз писал эмулятор PDP-8E на JavaScript, пытался запустить как можно больше программ на нём, когда увидел эту ветку форума с реализацией Форта для PDP-8. Я могу скомпилировать оригинальный исходник под TSS8, но в ней очень трудно дебаггить, потому что ОС постоянно висит на HLT и работает только от прерываний. Кроме того, я хотел попробовать реализовать минимальное ядро Форта на PDP-8 ассемблере и довести его до INTERPRET на самом Форте. Использовал PAL8 ассемблер под OS8.

Это листинг компиляции:

Code: Select all

0010         *10

00010 0000 TYPEI,  0              / TYPE INDEX
00011 0205 PCI,    TEST-1         / PC REGISTER

      0020         *20

00020 7777 C7777,  -1
00021 7501 RSP,    7501           / RETURN STACK POINTER (7400 - 7500)
00022 0400 CORSP,  -7400          / RSP STACK OVERFLOW CHECK
00023 0277 CURSP,  -7501          / RSP STACK UNDERFLOW CHECK
00024 7400 PSP,    7400           / PARAMETER STACK POINTER (7277 - 7377)
00025 0501 COPSP,  -7277          / PSP STACK OVERFLOW CHECK
00026 0400 CUPSP,  -7400          / PSP STACK UNDERFLOW CHECK

00027 0000 TEMP1,  0              / TEMP STORAGE

00030 0000 GETCH0, 0              / GET CHARACTER
00031 6031         KSF            / KEYBOARD FLAG SET?
00032 5031         JMP .-1        / NO, CHECK AGAIN
00033 6036         KRB            / YES, READ CHARACTER
00034 5430         JMP I GETCH0   / RETURN

00035 0000 PUTCH0, 0              / PRINT CHARACTER IN AC
00036 6041         TSF            / PRINTER FLAG SET?
00037 5036         JMP .-1        / NO, CHECK AGAIN
00040 6046         TLS            / YES, PRINT CHARACTER
00041 7300         CLA CLL        / CLEAR AC AND LINK
00042 5435         JMP I PUTCH0   / RETURN

00043 0000 TYPE0,  0              / PRINT MESSAGE IN AC
00044 1020         TAD C7777      / MSG-1
00045 3010         DCA TYPEI      / SETUP LOOP
00046 1410 TYPE1,  TAD I TYPEI    / GET CHAR
00047 7450         SNA            / <>0?
00050 5443         JMP I TYPE0    / NO, RETURN
00051 4035         JMS PUTCH0     / YES, PRINT CHAR
00052 5046         JMP TYPE1      / LOOP

00053 0000 ERROR0, 0              / PRINT MESSAGE AND STOP
00054 1053         TAD ERROR0     / GET MSG (AC = 0 ON ENTRY)
00055 4043         JMS TYPE0      / PRINT IT
00056 7402         HLT

00057 0000 RPUSH0, 0              / PUSH AC TO RETURN STACK
00060 3027         DCA TEMP1      / SAVE AC
00061 1021         TAD RSP        / GET SP
00062 1022         TAD CORSP      / STACK FULL?
00063 7640         SZA CLA
00064 5072         JMP RPUSH1     / NO, PUSH
00065 4053         JMS ERROR0     / YES, PRINT MESSAGE AND STOP
00066 0322         "R;"S;"F;0     / RETURN STACK FULL
00067 0323
00070 0306
00071 0000
00072 7040 RPUSH1, CMA            / -1
00073 1021         TAD RSP        / DEC SP
00074 3021         DCA RSP        / SAVE SP
00075 1027         TAD TEMP1      / RESTORE AC
00076 3421         DCA I RSP      / PUSH IT
00077 5457         JMP I RPUSH0   / RETURN

00100 0000 RPOP0,  0              / POP AC FROM RETURN STACK
00101 1021         TAD RSP        / GET SP (AC = 0 ON ENTRY)
00102 1023         TAD CURSP      / STACK EMPTY?
00103 7640         SZA CLA
00104 5112         JMP RPOP1      / NO, POP
00105 4053         JMS ERROR0     / YES, PRINT MESSAGE AND STOP
00106 0322         "R;"S;"E;0     / RETURN STACK EMPTY
00107 0323
00110 0305
00111 0000
00112 1421 RPOP1,  TAD I RSP      / POP IT
00113 2021         ISZ RSP        / INC SP (NEVER 0)
00114 5500         JMP I RPOP0    / RETURN

00115 0000 PPUSH0, 0              / PUSH AC TO PARAMETER STACK
00116 3027         DCA TEMP1      / SAVE AC
00117 1024         TAD PSP        / GET SP
00120 1025         TAD COPSP      / STACK FULL?
00121 7640         SZA CLA
00122 5130         JMP PPUSH1     / NO, PUSH
00123 4053         JMS ERROR0     / YES, PRINT MESSAGE AND STOP
00124 0320         "P;"S;"F;0     / PARAMETER STACK FULL
00125 0323
00126 0306
00127 0000
00130 7040 PPUSH1, CMA            / -1
00131 1024         TAD PSP        / DEC SP
00132 3024         DCA PSP        / SAVE SP
00133 1027         TAD TEMP1      / RESTORE AC
00134 3424         DCA I PSP      / PUSH IT
00135 5515         JMP I PPUSH0   / RETURN

00136 0000 PPOP0,  0              / POP AC FROM PARAMETER STACK
00137 1024         TAD PSP        / GET SP (AC = 0 ON ENTRY)
00140 1026         TAD CUPSP      / STACK EMPTY?
00141 7640         SZA CLA
00142 5150         JMP PPOP1      / NO, POP
00143 4053         JMS ERROR0     / YES, PRINT MESSAGE AND STOP
00144 0320         "P;"S;"E;0     / PARAMETER STACK EMPTY
00145 0323
00146 0305
00147 0000
00150 1424 PPOP1,  TAD I PSP      / POP IT
00151 2024         ISZ PSP        / INC SP (NEVER 0)
00152 5536         JMP I PPOP0    / RETURN

00153 1411 NEXT,   TAD I PCI      / GET NEXT WORD
00154 3027 NEXT1,  DCA TEMP1
00155 5427         JMP I TEMP1    / EXECUTE

00156 0000 DOCOL,  0              / EXECUTE USER-DEFINED WORD
00157 1011         TAD PCI        / GET PC
00160 4057         JMS RPUSH0     / PUSH TO RETURN STACK
00161 1156         TAD DOCOL      / GET ADDRESS AFTER THIS JMS DOCOL
00162 1020         TAD C7777      / ADDRESS-1
00163 3011         DCA PCI        / SET PC
00164 5153         JMP NEXT

00165 0000 DOCON,  0              / CONSTANT WORD
00166 1565         TAD I DOCON    / GET VALUE AFTER THIS JMS DOCON
00167 4115         JMS PPUSH0     / PUSH TO PARAMETER STACK
00170 5153         JMP NEXT

00171 0000 DOVAR,  0              / VARIABLE WORD
00172 1171         TAD DOVAR      / GET ADDRESS AFTER THIS JMS DOVAR
00173 4115         JMS PPUSH0     / PUSH TO PARAMETER STACK
00174 5153         JMP NEXT

      0200         *200

00200 6046 START,  TLS
00201 7300         CLA CLL
00202 5153         JMP NEXT

00203 7600 RESET,  7600
00204 5603 OS8,    JMP I RESET
00205 7402 STOP,   HLT

00206 0235 TEST,   SPFTC1; STOP;
00207 0205

/ DICTIONARY: PREV LINK, NAME LENGTH, NAME, CODE
00210 0000 FETCH,  0; 1; "@;                / @ (ADDR -- X) FETCH MEM AT ADDR
00211 0001
00212 0300
00213 1424 FETCH1, TAD I PSP      / GET ADDRESS AT TOS (AC = 0 ON ENTRY )
00214 3027         DCA TEMP1      / SAVE IT
00215 1427         TAD I TEMP1    / GET VALUE
00216 3424         DCA I PSP      / REPLACE TOS WITH VALUE
00217 5153         JMP NEXT

00220 0210 STORE,  FETCH; 1; "!;            / ! (X ADDR --) STORE X AT ADDR
00221 0001
00222 0241
00223 4136 STORE1, JMS PPOP0      / GET ADDR
00224 3027         DCA TEMP1      / SAVE IT
00225 4136         JMS PPOP0      / GET X
00226 3427         DCA I TEMP1    / SAVE X AT ADDR
00227 5153         JMP NEXT

00230 0220 SPFTC,  STORE; 3; "S;"P;"@;      / SP@ (-- SP) GET SP OF PARAMETER STACK
00231 0003
00232 0323
00233 0320
00234 0300
00235 1024 SPFTC1, TAD PSP        / GET SP
00236 4115         JMS PPUSH0     / SAVE IT
00237 5153         JMP NEXT

00240 0230 RPFTC,  SPFTC; 3; "R;"P;"@;      / RP@ (-- RP) GET SP OF RETURN STACK
00241 0003
00242 0322
00243 0320
00244 0300
00245 1021 RPFTC1, TAD RSP        / GET SP
00246 4115         JMS PPUSH0     / SAVE IT
00247 5153         JMP NEXT

00250 0240 ZEQ,    RPFTC; 2; "0;"=;         / 0= (X -- FLAG) -1 IF X IS 0, 0 OTHERWISE
00251 0002
00252 0260
00253 0275
00254 1424 ZEQ1,   TAD I PSP      / GET X AT TOS
00255 7650         SNA CLA        / <>0?
00256 1020         TAD C7777      / YES, SET -1
00257 3424         DCA I PSP      / REPLACE TOS WITH VALUE
00260 5153         JMP NEXT

00261 0250 PLUS,   ZEQ; 1; "+;              / + (X Y -- Z) SET Z = X + Y
00262 0001
00263 0253
00264 4136 PLUS1,  JMS PPOP0      / GET Y
00265 1424         TAD I PSP      / GET X AT TOS AND SUM
00266 3424         DCA I PSP      / REPLACE TOS WITH VALUE
00267 5153         JMP NEXT

00270 0261 NAND,   PLUS; 4; "N;"A;"N;"D;    / NAND (X Y -- Z) SET Z = !( X & Y)
00271 0004
00272 0316
00273 0301
00274 0316
00275 0304
00276 4136 NAND1,  JMS PPOP0      / GET Y
00277 0424         AND I PSP      / GET X AT TOS AND & WITH Y
00300 7040         CMA            / !AC
00301 3424         DCA I PSP      / REPLACE TOS WITH VALUE
00302 5153         JMP NEXT

00303 0270 EXIT,   NAND; 4; "E;"X;"I;"T;    / EXIT (R:ADDR --) RESUME EXECUTION AT RET ADDR
00304 0004
00305 0305
00306 0330
00307 0311
00310 0324
00311 4100 EXIT1,  JMS RPOP0      / GET R:ADDR
00312 3011         DCA PCI        / SET PC
00313 5153         JMP NEXT

00314 0303 KEY,    EXIT; 3; "K;"E;"Y;       / KEY (-- X) READ CHAR
00315 0003
00316 0313
00317 0305
00320 0331
00321 4030 KEY1,   JMS GETCH0     / GET CHAR
00322 4115         JMS PPUSH0     / SAVE IT
00323 5153         JMP NEXT

00324 0314 EMIT,   KEY; 4; "E;"M;"I;"T;     / EMIT (X --) PRINT CHAR
00325 0004
00326 0305
00327 0315
00330 0311
00331 0324
00332 4136 EMIT1,  JMS PPOP0      / GET X
00333 4035         JMS PUTCH0     / PRINT IT
00334 5153         JMP NEXT

00335 0324 LIT,    EMIT; 3; "L;"I;"T;       / LIT (-- X) GET NEXT CELL VALUE
00336 0003
00337 0314
00340 0311
00341 0324
00342 1411 LIT1,   TAD I PCI      / GET NEXT WORD
00343 4115         JMS PPUSH0     / SAVE IT
00344 5153         JMP NEXT

00345 0335 HERE,   LIT; 4; "H;"E;"R;"E;     / HERE VARIABLE
00346 0004
00347 0310
00350 0305
00351 0322
00352 0305
00353 4171 HERE1,  JMS DOVAR; FREE;
00354 0366

00355 0345 EXEC, HERE; 4; "E;"X;"E;"C;      / EXEC (SFA -- ) EXECUTE WORD
00356 0004
00357 0305
00360 0330
00361 0305
00362 0303
00363 4136 EXEC1,  JMS PPOP0      / GET SFA
00364 5154         JMP NEXT1      / EXECUTE

00365 0355         EXEC           / LAST WORD IN DICTIONARY
00366 0366 FREE,   .              / PTR TO FREE AREA

           $

COPSP  0025        TYPE0  0043
CORSP  0022        TYPE1  0046
CUPSP  0026        ZEQ    0250
CURSP  0023        ZEQ1   0254
C7777  0020
DOCOL  0156
DOCON  0165
DOVAR  0171
EMIT   0324
EMIT1  0332
ERROR0 0053
EXEC   0355
EXEC1  0363
EXIT   0303
EXIT1  0311
FETCH  0210
FETCH1 0213
FREE   0366
GETCH0 0030
HERE   0345
HERE1  0353
KEY    0314
KEY1   0321
LIT    0335
LIT1   0342
NAND   0270
NAND1  0276
NEXT   0153
OS8    0204
PCI    0011
PLUS   0261
PLUS1  0264
PPOP0  0136
PPOP1  0150
PPUSH0 0115
PPUSH1 0130
PSP    0024
PUTCH0 0035
RESET  0203
RPFTC  0240
RPFTC1 0245
RPOP0  0100
RPOP1  0112
RPUSH0 0057
RPUSH1 0072
RSP    0021
SPFTC  0230
SPFTC1 0235
START  0200
STOP   0205
STORE  0220
STORE1 0223
TEMP1  0027
TEST   0206
TYPEI  0010

ERRORS DETECTED: 0
LINKS GENERATED: 0
Это то, что я слепил на самом Форте, используя псевдо кросс-компилятор Форта, написанный быстро/грязно на JavaScript. Поддерживаются только 5 специальных символов, всё остальное записывается в память PDP-8 или как восьмеричное число, или заменяется числом из списка.
\ - комментарий до конца строки
^ слово число - добавить слово в список
~число - установить адрес памяти
: имя - генерирует заголовок для Форт dictionary
; - пишет код exit только для слов типа word

Code: Select all

^ word  4156            \                    0 1   2->
^ var   4171            \ word lit 5 lit 3 jmp 2 exit - jmp -3 (7775)
^ const 4165            \                      3->  2 1   0
^ test  0206
^ exec  0363
^ exit  0311
^ 0=    0254
^ emit  0332
^ ;     0311
^ @     0213
^ here  0353
^ key   0321
^ lit   0342
^ nand  0276
^ +     0264
^ rp@   0245
^ sp@   0235
^ !     0223

: dup  word sp@ @ ;
: inv  word dup nand ;
: and  word nand inv ;
: neg  word inv lit 1 + ;
: -    word neg + ;
: =    word - 0= ;
: <>   word = inv ;
: drop word dup - + ;
: over word sp@ lit 1 + @ ;
: swap word over over sp@ lit 3 + ! sp@ lit 1 + ! ;
: nip  word swap drop ;
: jmp  word rp@ @ dup lit 1 + @ + rp@ ! ;
: zjmp word 0= rp@ @ lit 1 + @ lit 1 - and rp@ @ lit 1 + + rp@ ! ;

: tib  const 7156 ;                         \ text buffer start (7156 - 7276)
: tib< const 0501 ;                         \ text buffer overflow (-7277)
: cr   const 0215 ;
: lf   const 0212 ;
: bl   const 0240 ;
: in<  var   0 ;                            \ end of text ptr
: >in  var   0 ;                            \ text ptr
: type word dup zjmp 16                     \ addr count --
            lit 1 - swap lit 1 + dup @ emit swap jmp 7761
            drop drop ;
: crlf word cr emit lf emit ;               \ --
: 2bl  word bl emit bl emit ;               \ --
: words word here @ lit 1 - @               \ --
             2bl dup lit 1 + dup @ type
             @ dup zjmp 3
             jmp 7763
             drop ;
: accept word lit 0 tib ! tib lit 1 + in< ! \ --
              key dup cr - zjmp 34 dup emit in< @ !
              tib @ lit 1 + tib ! in< @ lit 1 + in< !
              in< @ tib< + zjmp 4
              jmp 7740
              drop ;
: cmp   word over @ over @ = ;              \ a1 a2 -- a1   a2   flag
: nxt   word lit 1 + swap lit 1 + ;         \ a1 a2 -- a2+  a1+
: tmp   var  0 ;
: match word over @ lit 1 + tmp !           \ a1 a2 -- flag
             cmp zjmp 16
             tmp @ lit 1 - dup zjmp 14
             tmp ! nxt jmp 7761
             drop drop lit 0 jmp 6
             drop drop drop lit 7777 ;
: lookup word here @ lit 1 - @              \ str -- sfa | 0
              over over lit 1 +             \ str entry1 str entry1_name
              match zjmp 14                 \ str entry1
              lit 1 + dup @ + lit 1 + jmp 7 \ str sfa    / match
              dup zjmp 4 @ jmp 7750         \ str entryN / no match, next entry if not 0
              nip ;                         \ sfa | 0
: token word here @ lit 1 + dup tmp ! swap  \ addr -- addrN (init addr tib + 1)
             dup in< @ - zjmp 30            \ a0 addr        / check end of buffer
             dup @ dup bl - zjmp 20         \ a0 addr chr    / check delimiter
             tmp @ ! tmp @ lit 1 + tmp !    \ a0 addr        / copy chr to here area
             lit 1 + jmp 7745               \ a0 addrN       / next chr
             drop                           \ a0 addrN       / remove chr from stack
             swap tmp @ swap - here @ ! ;   \ addrN          / set str length
: >number word lit 0 tmp !                  \ str -- num     / acc = 0
               dup @                        \ str  len
               dup zjmp 51                  \ str  len       / check empty str       m4: jmp m1
               lit 1 - swap lit 1 +         \ len- str+
               over over @ lit 260 -        \ len- str+ len- dig
               over zjmp 20                 \ len- str+ len- dig  / check rank       m3: jmp m2
               dup + dup dup + dup + +      \ len- str+ len- dig*
               swap lit 1 - swap jmp 7757   \ len- str+ len- dig*                    jmp m3
               tmp @ + tmp ! drop           \ len- str+      / acc += dig*           m2:
               swap jmp 7726                \ str+ len-                              jmp m4
               drop drop tmp @ ;            \ num                                    m1:
: tm1   var  0 ;
: div10 word lit 260 tmp !                  \ n 10 -- n-10 c / tmp = char_0
             over over - dup lit 4000 nand  \ num 10 num-10 ~((num-10)&4000)         m2:
             lit 7777 = zjmp 20             \ num 10 num-10  / 0 if num <= 10        jmp m1
             tmp @ lit 1 + tmp !            \ num 10 num-10  / tmp += 1
             swap tm1 ! nip tm1 @ jmp 7746  \ num-10 10                              jmp m2
             drop drop                      \ num                                    m1:
             tmp @ ;                        \ num chr
: . word lit 1750 div10 emit                \ num --         / print bin->dec num 1000
         lit 144 div10 emit                 \                / print 100
         lit 12 div10 emit                  \                / print 10
         lit 1 div10 emit drop ;            \                / print 1
: interp word crlf lit 276 emit accept 2bl  \ --             / outer interpreter     m5:
              tib                           \ addr           / start address
              lit 1 + token >in ! here @    \ str            / get token             m3:
              dup @ zjmp 25                 \ str            / end of line?          jmp m4
              dup lookup dup zjmp 6         \ str sfa?       / try to find word, 0?  jmp m1
              swap drop exec jmp 3          \ --             / execute word, cont.   jmp m2
              drop >number                  \ -- num         / convert to number     m1:
              >in @ dup in< @ = zjmp 7741   \ addr           / end of buff?          m2: jmp m3
              drop jmp 7727 ;               \ --             / continue              m4: jmp m5

~0206 interp
AlexM1234
Junior
Posts: 4
Joined: 17 Dec 2025 12:09

Re: Forth на PDP-8

Post by AlexM1234 »

Экран эмулятора PDP-8 с INTERPRET
You do not have the required permissions to view the files attached to this post.
Last edited by AlexM1234 on 20 Dec 2025 01:50, edited 2 times in total.
AlexM1234
Junior
Posts: 4
Joined: 17 Dec 2025 12:09

Re: Forth на PDP-8

Post by AlexM1234 »

Добавил URL https://github.com/alex-code1234/emu8/b ... _forth.png в img тэг, а картинки всё равно нет...
AlexM1234
Junior
Posts: 4
Joined: 17 Dec 2025 12:09

Re: Forth на рк-86

Post by AlexM1234 »

Вообще-то это было как бы продолжение этого топика:
Lavr wrote: 17 May 2020 22:07 P.S. И я тут разыскал исходник Форта для PDP-8 исторических времён, не новодел:
b2m wrote: 23 May 2020 05:22
Lavr wrote:А там где есть и аппаратный стек, и развитая косвенная адресация (как в z80) - там удивляться
особо нечему...
Между прочим, наличие у PDP-8 автоинкрементной индексации позволяет сделать форт с прямым шитым кодом, который будет гораздо эффективнее того, что реализовано Джоном.

Здесь мы имеем такой код (предположим скомпилированы слова WORD1 WORD2):

Code: Select all

JMS I [CALL0]
WORD1
JMS I [CALL0]
WORD2
В конце каждого слова идет вызов JMS I [RET0], исходный код этих процедур я приводить не буду, но действий там не мало.

А теперь рассмотрим гипотетический прямой шитый код:

Code: Select all

WORD1
WORD2
Предположим, что текущий указатель на форт-программу у нас в INDEX1. Тогда адресный интерпретатор сократится лишь до 4-х команд:

Code: Select all

CLA
TAD I INDEX1
DCA TEMP1
JMP I TEMP1
Эти команды нужно ставить в конце ассемблерных слов вместо RET, ну или оформить в виде процедуры NEXT.
Конечно, как и в любом форте с прямым шитым кодом, форт-слово будет начинаться вызовом JMS I [CALL], а завершаться словом EXIT. Но учитывая, что в конечном счёте чаще всего будут вызываться ассемблерные слова, в которых этих вызовов конечно нет, то исполнение будет гораздо быстрее. И это не учитывая того, что прямой шитый код в два раза компактнее.
User avatar
Shaos
Admin
Posts: 24715
Joined: 08 Jan 2003 23:22
Location: Silicon Valley

Re: Forth на PDP-8

Post by Shaos »

Это старые сообщения - лучше всё-таки в новом топике (перенёс вышестоящее сообщение сюда тоже - можно скопировать цитаты в первый пост)

P.S. Прицепил картинку прямо в сообщение - внешние картинки сюда ссылками лучше не тянуть

P.P.S. Перенёс цитаты из предыдущего сообщения в первый пост, также перенеся spoiler из цитируемого поста...