ALWAYS 22SEP6 READ ;01SEP6 .MCALL DIR$,GET$S,OPEN$R,CLOSE$,CALLR,QIOW$ ;************************************************************************ ;* * ;* MODULE: READ * ;* * ;* FUNCTION: READ HEX INPUT FILE * ;* * ;* INPUT PARAMETERS: * ;* * ;* R0 POINTS TO THE COMMAND LINE IN PROCESS * ;* * ;* OUTPUT PARAMETERS: * ;* * ;* R0 POINTS JUST BEYOND COMMAND LINE * ;* * ;* DESTROYS: ALL REGISTERS * ;* * ;* AUTHOR: KEVIN ANGLEY * ;* * ;* DATE: 24-AUG-82 * ;* * ;* REVISED BY: Chris Doran, Sira Ltd. * ;* * ;* DATE: Dec83-Feb84 * ;* * ;* MODIFICATIONS: * ;* Add extra formats: ROCKWELL, RCA, TEXAS, WHITESMITHS, * ;* RIM, BIN, HEX, OCTAL, SIRA, OBJECT, TASK, ABSOLUTE * ;* with originals rewritten to use common subroutines. * ;* Support EVEN, ODD, and STEP options to read back * ;* hi/lo PROM files etc., by calling standard FROMTH * ;* subroutine if READ is followed by FROM, ODD, or EVEN. * ;* Change GET$ to GET$S for overlaid version. * ;* Support program name specification where appropriate. * ;* Don't print start address/name if not given. * ;* Suppress statistics printout if NOECHO selected. * ;* 2-Mar-84 CJD * ;* Add FAIRCHILD option. * ;* Support HEX/OCTAL SMS format, and allow ^A as well as * ;* ^B as data start flag in "normal" mode. * ;* Rewrite RCA format, according to the formal COSMAC UT4 * ;* utility specification. * ;* Correct actual/potential errors in MOSTEK and EXTENDED * ;* due to byte flags being referenced by word instrs. * ;* * ;* 23MAR4 Scott Smith, Telex Computer Products, Raleigh, NC * ;23MAR4 ;* Included 'USE FOR READ' modifications. * ;23MAR4 ;* * ;23MAR4 ;* 29-Mar-85 CJD * ;01APR5 ;* Merge 2-Mar-84 with 23MAR4 * ;01APR5 ;* Support ctrl/C trap on compare (only). * ;01APR5 ;* Put error code into the right place in IOE at IOERROR. * ;01APR5 ;* Add WITH option to COMPARE (only) 2 ranges of memory. * ;01APR5 ;* Extend Intel to support "extended address" (02) and * ;01APR5 ;* "transfer address" (03) records. * ;01APR5 ;* Correct TASK file read when last block excatly full -- * ;01APR5 ;* used to try to read one more block, giving error. * ;01APR5 ;* * ;01SEP6 ;* 1-Sep-86 CJD * ;01SEP6 ;* Correct bug in Whitesmiths' read: must force read of * ;01SEP6 ;* a record the first time around. * ;01SEP6 ;* * ;22SEP6 ;* 22-Sep-86 CJD * ;22SEP6 ;* Return transfer address=0 always in Whitesmiths' read. * ;22SEP86 ;* * ;************************************************************************ READ:: CLRB CMPFLG ; ENTRY POINT FOR READ - INDICATE NOT COMPARE BR READCMP ; BRANCH TO COMMON CODE COMPARE:: MOVB @%0,CMPFLG ; ENTRY POINT FOR COMPARE - INDICATE COMPARE MOV #IO.ATA,ATTDET+Q.IOFN ; Attach TI: so ^O/^C will work ;01APR5 DIR$ #ATTDET ; if we have a lot of differences READCMP: MOV #IO.DET,ATTDET+Q.IOFN ; (Re)set DPB to detach for exit PUSH RWFORMAT ; Save format in case COMPARE changes it ;01APR5 MOV SP,RETSP ; Save SP for abnormal error exits MOVB #377,ERRFLG ; Set error flag <> 0 .IIF DF P$$OFF, CLRB WRNFLG ; Clear warning flag CLRB MOSTYP ; INDICATE THAT MOSTEK HEADER RECORD NOT YET FOUND CLRB TRAILER ; INDICATE THAT TRAILER RECORD NOT YET FOUND CLRL ADDVAL ; ASSUME NO OFFSETTING UPON READING ADDRESSES CLRB PART ; ASSUME NO PARTIAL READ CLR BCOUNT ; CLEAR COUNT OF BYTES LOADED THIS READ CLR DCOUNT ; Clear count of differences found MOV #1,STEP ; Default STEP to 1 if no range specification CLRL LOBOUND ; and lo bound to 0, MOV #177777,HIBOUND ; high bound to FFFFFFFF MOV #177777,HIBOUND+2 MOV RDDOM,USECNT ; Move domain into 'USE FOR READ' counter ;23MAR4 .PAGE .SBTTL COLLECT KEYWORDS GETKEY FROM ; IS FROM/THRU OPTION THERE? BEQ 12$ ; Yes, go call FROMTH GETKEY EVEN ; Range may also start EVEN BEQ 12$ GETKEY ODD ; Or ODD BNE 14$ ; Default to whole if none of these INC %0 ; Make any of them advance pointer by 4 chars 12$: SUB #4,%0 ; Point back to range start CALL FROMTH ; Get range like everybody else BCS 140$ ; Trap error 14$: GETKEY PLUS ; TRY FOR PLUS KEYWORD BNE 141$ ; NE: NO GOTS CALL GETHXL ; GET THE PLUS ADDRESS BCC 144$ ; CC: GOT IT 140$: JMP ERREXIT ; TAKE ERROR EXIT 141$: GETKEY MINUS ; TRY FOR MINUS KEYWORD BNE 148$ ; NE: NO GOTS CALL GETHXL ; GET THE MINUS ADDRESS BCC 143$ ; CC: GOT IT BR 140$ ; (ERREXIT) ; TAKE ERROR EXIT 143$: NEG R2 ; NEGATE THE MINUS ADDRESS NEG R1 SBC %2 ; in double-precision 144$: MOV R1,ADDVAL ; SET UP ADDVAL MOV R2,ADDVAL+2 148$: GETKEY PARTIAL ; TRY FOR PARTIAL READ BNE 153$ ; NE: NO GOTS INCB PART ; SET PARTIAL FLAG 153$: GETKEY FILE ; GET THE KEYWORD "FILE" BEQ 16$ ; EQ: GOT IT TSTB CMPFLG ; NE. Is this a COMPARE? ;01APR5 BEQ 154$ ; No, error ;01APR5 GETKEY WITH ; Yes, WITH is also allowed ;01APR5 BNE 154$ ; Anything else is an error ;01APR5 CALL GETHXL ; Get WITH start address ;01APR5 BCS 140$ ; (ERREXIT) ; Take error exit ;01APR5 MOV R1,R3 ; Copy address ;01APR5 MOV R2,R4 ;01APR5 CALL ADDADV ; Add PLUS/MINUS offset ;01APR5 CALL VALID ; Offset and validate ;01APR5 BCS 140$ ; (ERREXIT) ; Take error exit ;01APR5 CALL EXTRA ; Check for junk ;01APR5 BCS 140$ ; (ERREXIT) ; Take error exit ;01APR5 MOV #F.MAX+2,RWFORMAT ; Bodge format to our special case ;01APR5 MOV R2,R0 ; Load unoffsetted WITH address ;01APR5 BR 20$ ; Join common code ;01APR5 154$: OUTPUT MSK ; MISSING KEYWORD BR 140$ ; (ERREXIT) ; TAKE ERROR EXIT 16$: CALL PARSE ; PARSE THE FILENAME BCS 140$ ; (ERREXIT) ; TAKE ERROR EXIT OPEN$R #FDB ; OPEN THE FILE FOR READ BCC 20$ ; CC: SUCCESS MOV #FOE+FOELEN-4,%0 ; FILE OPEN ERROR MOV FDB+F.ERR,%1 ; Get error code CALL PUTHX4 ; Put in error message OUTPUT FOE ; BR 140$ ; (ERREXIT) ; TAKE ERROR EXIT 20$: MOV #177777,LOADDR ; LOWEST ADDRESS ENCOUNTERED SO FAR IS FFFFFFFF MOV #177777,LOADDR+2 CLRL HIADDR ; CLEAR HIGHEST ADDRESS ENCOUNTERED SO FAR ; (LONG WORD) MOV #RNM+RNMLEN,%5 ; Address area for program name read MOV #8.,%2 ; Load count 21$: MOVB #SPACE,-(%5) ; Fill it with spaces SOB %2,21$ .PAGE .SBTTL INPUT SECTION ; ; REGISTER USAGE: ; ; R0 POINTS TO CURRENT LOCATION IN RECORD ; R1 CONTAINS VALUE BEING PROCESSED ; R2 CONTAINS CURRENT ADDRESS BEING FILLED (OFFSETTED) ; R3 HOLDS THE REAL ADDRESS (LOW WORD) ; R4 HOLDS THE REAL ADDRESS (HIGH WORD) ; R5 CONTAINS THE NUMBER OF DATA BYTES YET TO PROCESS THIS RECORD ; MOV RWFORMAT,%1 ; Load file format code BIT #1,%1 ; Do a QA check to avoid crash BNE 22$ ; Code mustn't be odd CMP %1,#F.MAX+2 ; or > max + 2 (or -2) ;01APR5 BLOS 50$ ; OK, go read .IIF NDF TCI, RTCI=. 22$: OUTPUT UFS ; "Unsupported format" ; THIS NEVER SHOULD HAPPEN JMP CLOSE ; TAKE ERROR EXIT WITH CLOSE 50$: MOV FORMTS(%1),%1 ; Fetch read routine and format flag BIC #1,%1 ; Lose binary bit flag (0) CALL @%1 ; Read defined type .PAGE .SBTTL REPORT ON FILE READ ; Return here, if successful, with: ; RNM name (if any, spaces still if not) ; %3/%4 transfer address, including ADDVAL, (0 if none) ; TRAILER <> 0 if we have had a trailer record. ; Issue a (non-fatal) error message if PART is <> 0 (no trailer ; allowed) but we have one anyway. The case of PART = 0 (trailer ; expected) and TRAILER = 0 (not found) is dealt with at NOTRAIL. REPORT: DIR$ #ATTDET ; Detach TI: in case ctrl/O'd CLRB ERRFLG ; No serious errors found (yet!) TSTB PART ; Partial READ? BEQ 40$ ; No, allow a trailer TSTB TRAILER ; Yes, did we get one? BEQ 40$ ; No, OK OUTPUT TRI ; "Trailer record ignored" .IIF DF P$$OFF, INCB WRNFLG ; Flag a warning CLR %3 ; Lose new transfer address CLR %4 40$: TSTB CMPFLG ; Compare? BEQ 51$ ; No, don't report no of differences MOV DCOUNT,%1 ; Get no of differences BNE 42$ ; Always print if non-zero TST QUIET ; But if messages suppressed BEQ 51$ ; Don't print redundant 0 42$: MOV #DCT+1,%0 ; Address differences count position CALL PUTHX4 ; in message MOVB #'s,DCT+DCTLEN-1 ; Assume count <> 1 DEC %1 ; But if it is BNE 50$ CLRB DCT+DCTLEN-1 ; suppress the 's' on differences 50$: OUTPUT DCT ; Print no of differences 51$: MOV #PRGNAM,%2 ; Address space for {new} name MOV #RNM+RNMLEN-8.,%0 ; and name just read CMPB @%0,#SPACE ; Any name read? BEQ 60$ ; No, skip store/check/print MOV #8.,%5 ; Yes, load length TSTB CMPFLG ; Is this a compare? BEQ 54$ ; No, read, store name 52$: CMPB (%0)+,(%2)+ ; Else compare bytes BNE 53$ ; Trap mismatch SOB %5,52$ ; Check whole name BR 55$ ; OK if all matches 53$: OUTPUT FNM,#FNMLEN+RNMLEN ; Else print file and current names .IIF DF P$$OFF, INCB WRNFLG ; Warn if different BR 60$ ; Go deal with transfer address 54$: MOVB (%0)+,(%2)+ ; New name, just copy it SOB %5,54$ 55$: TST QUIET ; Unless display suppressed, BEQ 60$ OUTPUT FNM ; print it 60$: MOV #RSSLEN,SAVLEN ; Short message if no transfer ; Subtract ADDVAL from transfer address (if any), double-precision. PUSH %3 ; First see if there is one BIS %4,(SP)+ BEQ 2270$ ; None, skip compare/store SUB ADDVAL,%3 ; Yes, remove ADDVAL SBC %4 SUB ADDVAL+2,%4 ; in double-precision MOV #RSMLEN,SAVLEN ; Have an xfer addr, so o/p full mesg TST CMPFLG ; IS THIS A COMPARE? BEQ 63$ ; EQ: NO, UPDATE TRANSFER VALUE CMP TRNSFR,R3 ; IS LOW WORD IDENTICAL? BNE 62$ ; NE: NO CMP TRNSFR+2,R4 ; IS HIGH WORD IDENTICAL? BEQ 227$ ; EQ: YES 62$: MOV #RDT+RDTLEN-8.,R0 ; FORMAT MESSAGE MOV TRNSFR,R1 ; WITH STORED VALUE MOV TRNSFR+2,R2 CALL PUTHXJ MOV R3,R1 ; PUT FILE VALUE INTO MESSAGE MOV R4,R2 MOV #FLT+FLTLEN-8.,R0 CALL PUTHXJ OUTPUT RDT,#RDTLEN+FLTLEN ; OUTPUT THE MESSAGE .IIF DF P$$OFF, INCB WRNFLG ; Warn if different BR 227$ ; AND CONTINUE 63$: MOV R3,TRNSFR ; UPDATE TRANSFER ADDRESS (LOW WORD) MOV R4,TRNSFR+2 ; UPDATE TRANSFER ADDRESS (HIGH WORD) 227$: MOV TRNSFR,R1 ; PUT TRANSFER ADDRESS IN MESSAGE MOV TRNSFR+2,R2 MOV #RDT+RDTLEN-8.,R0 CALL PUTHXJ 2270$: TST BCOUNT ; WAS THERE ANY DATA REALLY LOADED? BNE 228$ ; NE: ABSOLUTELY CLRL LOADDR ; EQ: NOT ANY, MUST CLEAR STATISTICS 228$: MOV LOADDR,R1 ; PUT LOWEST ADDR ENCOUNTERED IN MESSAGE MOV LOADDR+2,R2 MOV #RDL+RDLLEN-9.,R0 CALL PUTHXJ MOV HIADDR,R1 ; PUT HIGHEST ADDR ENCOUNTERED IN MESSAGE MOV HIADDR+2,R2 MOV #RDH+RDHLEN-9.,R0 CALL PUTHXJ MOV BCOUNT,R1 ; PUT BYTE COUNT INTO MESSAGE MOV #RDC+RDCLEN-5,R0 CALL PUTHX4 ; COMPUTE 16-BIT BYTE-WISE CHECKSUM OVER ; ENTIRE RANGE OF LOW - HIGH MOV LOADDR,R3 ; PUT LOW ADDRESS IN R3/R4 MOV LOADDR+2,R4 CALL VALID ; SUBTRACT OFFSET MOV R2,R5 ; FROM ADDRESS IN R5 MOV HIADDR,R3 ; PUT HIGH ADDRESS IN R3/R4 MOV HIADDR+2,R4 CALL VALID ; SUBTRACT OFFSET SUB R5,R2 ; PUT COUNT IN R2 INC R2 PUSH R5 ; SAVE FROM ADDRESS TEMPORARILY ON STACK MOV R2,R5 ; KEEP RUNNING COUNT IN R5 POP R2 ; KEEP CURRENT ADDRESS IN R2 CLR CSUM ; CLEAR TOTAL CHECKSUM 240$: CALL UNOFFSET ; TEMPORARILY UNOFFSET SO WE CAN VALIDATE CALL VALID ; MUST KEEP A VALID POINTER THROUGHOUT BCS 245$ ; CS: WENT INVALID - CANNOT COMPUTE CHECKSUM MOVB MEMORY(R2),R0 ; GET BYTE TO ADD BIC #177400,R0 ; CLEAR HIGH BYTE ADD R0,CSUM ; ADD THE NEW BYTE TO THE CHECKSUM INC R2 ; INCREMENT VIRTUAL MEMORY POINTER SOB R5,240$ ; REPEAT FOR COUNT MOV CSUM,R1 ; PUT SUM INTO R1 TST BCOUNT ; WAS THERE ANY DATA REALLY LOADED? BNE 241$ ; NE: ABSOLUTELY CLR R1 ; EQ: NO - JUST SHOW SUM OF 0000 241$: MOV #RDS+RDSLEN-5,R0 CALL PUTHX4 ; PUT OUT SUM DATA 243$: TST QUIET ; Did all that to set up HI/LOADDR BEQ CLOSE ; But don't print if quiet mode selected MOV #RSM,OUTDIR+Q.IOPL ; Set up statistics message, MOV SAVLEN,OUTDIR+Q.IOPL+2 ; length SAVLEN BR OUT ; AND CLOSE IT UP 245$: MOV #RDS+RDSLEN-5,R0 ; SHOW "????" FOR CHECKSUM MOV #4,R1 ; PUT 4 "?" 247$: MOVB #'?,(R0)+ SOB R1,247$ BR 243$ IOERROR: MOV #IOE+IOELEN-4,%0 ; File I/O error ;01APR5 MOV FDB+F.ERR,%1 ; Get the error code CALL PUTHX4 ; Insert in message MOV #IOE,OUTDIR+Q.IOPL MOV #IOELEN,OUTDIR+Q.IOPL+2 OUT: DIR$ #OUTDIR ; OUTPUT CLOSE: CLOSE$ #FDB ; EXIT WITH CLOSE ERREXIT: MOV RETSP,SP ; Get stack right after abnormal exit POP RWFORMAT ; Restore format, maybe changed by COMPARE ;01APR5 DIR$ #ATTDET ; Detach TI: if {still} attached .IF DF P$$OFF ; Set HEX's exit status to WARNING if missing/unexpected trailer found, ; or COMPARE found a difference in name or transfer address. TSTB WRNFLG ; Warning required? BEQ 10$ ; No, just test for error DEC EXSTAT ; Yes, try to change success (1) to warning (0) BEQ 10$ ; Done if we get 0 INC EXSTAT ; If not, restore exit status to error 10$: .ENDC NEGB ERRFLG ; Set carry if error flag <>0 BCS 20$ NEG DCOUNT ; Or difference count <>0 20$: RETURN ; for final return ; Warning exit point for a format that expects a trailer record containing a ; start address, but finds EOF first. Message is suppressed if a PARTIAL ; file was declared. NOTRAIL:TSTB PART ; IF PARTIAL, NO ERROR MESSAGE BNE NOXFER ; NE: OK OUTPUT TRL ; NO TRAILER RECORD .IIF DF P$$OFF, INCB WRNFLG ; Set warning ; (Common) return point for formats which have no start address or trailer ; -- just finish at EOF. NOXFER: CLR %3 ; But no SA CLR %4 JMP REPORT ; Complete as if file read fully otherwise CSERROR: ; Checksum error exit: OUTPUT CSE ; Print message, and record: ; File read error exit. Display faulty record if ASCII, but not if binary. ERROR: 10$: MOV RWFORMAT,%1 ; Fetch format type BIT #1,FORMTS(%1) ; If format read routines entry is off BNE CLOSE ; This is binary. Can't display. just close MOV #RECORD,OUTDIR+Q.IOPL ; SET UP FOR OUTPUT MOV SAVLEN,OUTDIR+Q.IOPL+2 ; USE SAVED LENGTH BR OUT ; Show record HCERROR: ; Conversion error: OUTPUT HCE ; Print message BR ERROR ; and record ITERROR: OUTPUT IBT ; "Incorrect block type" BR ERROR BCERROR: OUTPUT IBC ; "Incorrect block count" BR ERROR .PAGE .SBTTL GET FILE RECORD ; Get next record from the file. Return to call+6 if all OK, to address ; in call+4 if EOF, else abnormally to IOERROR. ; ; On successful exit, %1 and SAVLEN contain record length, %0 addresses ; its start, and flags are set on length. GETREC: GET$S #FDB ; Get a record BCC 5$ ; Process if OK CMPB F.ERR(%0),#IE.EOF ; End-of-file? BNE IOERROR ; No, some other error MOV @(SP)+,-(SP) ; Take EOF action -- push address to handle it JMP @(SP)+ ; Return there 5$: ADD #2,@SP ; Normal return, skip EOF address MOV F.NRBD(%0),%1 ; Return record size MOV F.NRBD+2(%0),%0 ; and address MOV %1,SAVLEN ; Save length in case of error, and set flags RTS PC ; and exit ; Most common cases are a branch to "No trailer" error if eof found first: GETNOT: CALL GETREC ; Get record NOTRAIL ; Process early EOF RETURN ; Return to caller otherwise ; And EOF = trailer, for formats that don't have one: GETEND: CALL GETREC ; Get record NOXFER ; EOF -- just set transfer addr = 0 RETURN ; Return if not EOF .PAGE .SBTTL GET DATA BYTES/WORDS ; Read next two digits as a hex byte from the input record. Take error exit ; if unsuccessful, else and add to checksum, and return to caller with ; flags set on current value of checksum (0 at end of Intel record). ; Entry GETCSM is used to fetch checksum byte, which should equal total so far. GETCSM: NEG CSUM ; Negate checksum so far GETBYT: CALL GETHX2 ; Get hex byte BCC ADDBYT ; OK, add into {16-bit} checksum & return to caller BR ERROR ; Take error exit if it fails ; Read next four digits as a hex word, taking error exit as GETBYT on failure, ; or add (byte-wise) to checksum and return to caller. GETWRD: CALL GETBYT ; Get hi byte PUSH %1 ; Save it CALL GETBYT ; Get lo .IF DF M$$EIS BR ADDHI ; OR in stacked hi and return .IFF JMP ADDHI ; OR in stacked hi and return .ENDC ; Binary reads, for various binary object formats. ; Read a byte, checking for end-of-record. BINCSM: NEG CSUM ; Checksum test entry, like GETBYT BINBYT: DEC SAVLEN ; Decrement count BPL 10$ ; Off end of record if it becomes -ve CALL GETNOT ; Get another if so, shouldn't reach EOF BR BINBYT ; Try again while more data 10$: MOVB (%0)+,%1 ; Else fetch byte ADDBYT: BIC #^C377,%1 ; only ADD %1,CSUM ; Add to checksum TSTB CSUM ; Set flags on lo byte of checksum RETURN ; Return to caller or exit ; Read two bytes into a word, lo byte first. BINWRD: CALL BINBYT ; Get lo byte PUSH %1 ; Save lo byte CALL BINBYT ; Whilst fetching hi SWAB %1 ; Shift to hi byte BISB (SP)+,%1 ; OR in lo RETURN ; return with full word ; Tektronix-type reads -- checksum is formed from hex nybbles. DIGBYT: CALL GETBYT ; Get byte in the normal way PUSH %1 ; Save value BIC #^B1111,%1 ; Lose lo nybble SUB %1,CSUM ; Remove hi from checksum ASH #-4,%1 ; Shift hi nybble to lo ADD %1,CSUM ; Add in lo nybble POP %1 ; Restore %1 RETURN ; Return with it ; Get word similarly. DIGWRD: CALL DIGBYT ; Fetch hi byte PUSH %1 ; Save it CALL DIGBYT ; Fetch lo ADDHI: SWAB @SP ; Put 1st byte in hi byte on stack BIS (SP)+,%1 ; OR together RETURN ; and return ; Copy address in %1 to %3/%4, adding ADDVAL DPADR: MOV %1,%3 ; %1 is lo CLR %4 ; Unsigned sign extend ; Add ADDVAL to %3/%4. ADDADV: ADD ADDVAL,%3 ; Add lo ADC %4 ; and carry ADD ADDVAL+2,%4 ; Add hi RETURN ; Get a number of any number of digits, until terminator, addressed by %0 ; on exit. Result to %1 (lo) and %2 (hi). ; Routine address in DGTSUB checks and adjusts for hex or octal digit. GETNUM: PUSH %3 ; Save %3 CLR %3 ; Clear result CLR %2 ; %2 = hi, %3 = lo (internally, for ASHC) 10$: MOVB @%0,%1 ; Get character SUB #'0,%1 ; Digit? CALL @DGTSUB ; Check for hex or octal as required BCS 20$ ; Done if not a digit .IF DF M$$EIS ASHC #3,%2 ; Shift up result so far .IFF .REPT 3 ; Shift up result so far ASL %3 ROL %2 .ENDR .ENDC BIS %1,%3 ; OR in binary value INC %0 ; Point to next character BR 10$ ; Try that 20$: MOV %3,%1 ; Copy result lo to %1 POP %3 ; Restore %3 RETURN ; Exit with value ; Hex digit test. (CAUTION: also doubles %2/%3). HEXDGT: CMP %1,#9. ; <= 9? BLOS 10$ ; Yes, digit, go add CMP %1,#'A-'0 ; No, was it 'A'? BLO 20$ ; No, found terminator, exit cs from CMP CMP #'F-'0,%1 ; was it <= 'F'? BLO 20$ ; No, terminator SUB #'A-10.-'0,%1 ; Yes, convert digit to binary 10$: ASL %3 ; Do one shift now, ROL %2 ; 3 more done by caller CLC ; Say OK 20$: RETURN ; return ; Octal digit test. OCTDGT: CMP #7,%1 ; Set carry if > 7 RETURN .PAGE .SBTTL STORE OR COMPARE BYTE ; Inputs: ; %1 (lo) Byte value just read ; %3/%4 Real address to store in, including OFFSET and ADDVAL ; ; Outputs: ; %2 is unoffsetted address ; %3/%4 Address for next byte (%3/%4 input + STEP) ; LOADDR Revised lowest address ; HIADDR Revised highest address ; ; %1 is preserved (byte-swapped on STHCMP entry. ; ; Combines LOOK, PUTBYT, and MISMATCH plus common calling code, from Aug 82 ; version. STHCMP: SWAB %1 ; Entry to do hi byte STOCMP: TSTB USECNT ; Are we counting bytes? ;23MAR4 BEQ 40$ ; EQ: No - proceed ;23MAR4 DECB USECNT ; Counting bytes so decrement count ;23MAR4 BNE 29$ ; (65$) ; Not using this byte - exit ;01APR5 TST USECNT ; Is this the range count? (high byte = 1?) ;23MAR4 BEQ 30$ ; EQ: Domain count - continue processing ;23MAR4 MOV RDDOM,USECNT ; Reset the domain count ;23MAR4 29$: JMP 65$ ; and exit ;01APR5 30$: TSTB RDRAN ; Are the domain and range equal? (ran-dom=0)vvv;23MAR4 BEQ 35$ ; YES - Move on ;23MAR4 MOV RDRAN,USECNT ; NO - Move range count into counter ;23MAR4 BR 40$ ; and process the byte ;23MAR4 35$: MOV RDDOM,USECNT ; reset the domain count and process the byte ;23MAR4 40$: CMP LOBOUND+2,R4 ; WITHIN RANGE? ;23MAR4 BNE 41$ ; If hi words differ, that determines it CMP LOBOUND,R3 ; HIGH WORDS SAME - HOW ABOUT LOW WORD? 41$: BHI 421$ ; (60$) ; OUT OF RANGE - IGNORE THIS DATA BYTE ;01APR5 CMP HIBOUND+2,R4 ; WITHIN RANGE? BNE 42$ CMP HIBOUND,R3 ; HIGH WORDS SAME - HOW ABOUT LOW WORD? 42$: BHIS 422$ ;01APR5 421$: JMP 60$ ; OUT OF RANGE - IGNORE THIS DATA BYTE ;01APR5 422$: ;01APR5 CMP LOADDR+2,R4 ; IS THIS A NEW LOW? BNE 45$ CMP LOADDR,R3 ; EQ: MAYBE - CHECK LOW WORD 45$: BLOS 46$ ; LO: DEFINITELY NO MOV R3,LOADDR ; UPDATE NEW LOW ADDRESS (LOW WORD) MOV R4,LOADDR+2 ; UPDATE NEW LOW ADDRESS (HIGH WORD) 46$: CMP HIADDR+2,R4 ; IS THIS A NEW HIGH? BNE 47$ CMP HIADDR,R3 ; EQ: MAYBE - CHECK LOW WORD 47$: BHIS 48$ ; HI: DEFINITELY NO MOV R3,HIADDR ; UPDATE NEW HIGH ADDRESS (LOW WORD) MOV R4,HIADDR+2 ; UPDATE NEW HIGH ADDRESS (HIGH WORD) 48$: INC BCOUNT ; Update total count CALL VALID ; Validate real address, 16-bit offset into %2 BCC 49$ ; OK, store byte JMP ERROR ; Errors are fatal (message already displayed) 49$: TSTB CMPFLG ; IS THIS JUST A COMPARE? BEQ 50$ ; EQ: NO, PROCESS LIKE A READ TSTB ABRQST ; Yes, has ctrl/C been hit? ;01APR5 BEQ 491$ ; No, continue ;01APR5 OUTPUT ABT ; Yes, say "Aborted by ctrl/C" ;01APR5 JMP CLOSE ; And stop now ;01APR5 491$: ;01APR5 CMPB R1,MEMORY(R2) ; DOES IT MATCH? BEQ 60$ ; CONTINUE LIKE A READ INC DCOUNT ; Increment differences count ; Show mismatch: ; ; R2 CONTAINS THE OFFSETTED ADDRESS ; R3/4 CONTAINS THE UN-OFFSETTED ADDRESS ; R1 CONTAINS THE VALUE OBTAINED FROM THE FILE ; PUSH R1 ; SAVE FILE VALUE PUSH R2 ; SAVE OFFSETTED VALUE PUSH R0 ; SAVE READ POINTER ;01APR5 MOV #AMF+39.,R0 ; PUT FILE VALUE INTO MESSAGE CALL PUTHAS ; In hex and ASCII MOVB MEMORY(R2),R1 ; GET MEMORY VALUE MOV #AMF+27.,R0 ; PUT INTO MESSAGE CALL PUTHAS ; In hex and ASCII MOV R3,R1 ; SET UP UN-OFFSETTED 32-BIT ADDRESS MOV R4,R2 MOV #AMF+10.,R0 ; PUT INTO MESSAGE CALL PUTHXJ CMP RWFORMAT,#F.MAX+2 ; Is this COMPARE ... WITH? ;01APR5 BNE 492$ ; No, standard treatment ;01APR5 MOV #'$,OUTDIR+Q.IOPL+4 ; Yes, prompt c-c for 1st part of message ;01APR5 OUTPUT AM1 ; Output first part of message ;01APR5 MOV 4(SP),R1 ; Get memory value for second part ;01APR5 MOV #AMF+^D27,R0 ; Put WITH area value into message ;01APR5 CALL PUTHAS ; In hex and ASCII ;01APR5 MOV OFFST,R1 ; Load OFFSET ;01APR5 MOV OFFST+2,R2 ; in double-precision ;01APR5 ADD (SP),R1 ; Add WITH area address ;01APR5 ADC R2 ; (As UNOFFSET, but with different regs) ;01APR5 MOV #AMF+^D10,R0 ; Put into message ;01APR5 CALL PUTHXJ ;01APR5 MOV #'+,OUTDIR+Q.IOPL+4 ; 2nd part is appended ;01APR5 OUTPUT AM2 ; Output ;01APR5 MOV #SPACE,OUTDIR+Q.IOPL+4 ; Restandardise DPB ;01APR5 BR 493$ ; Done ;01APR5 492$: OUTPUT AMF ; PUT THE MESSAGE OUT 493$: ;01APR5 POP R0 ; RESTORE READ POINTER ;01APR5 POP R2 ; RESTORE THE OFFSETTED VALUE POP R1 ; RESTORE THE FILE VALUE BR 60$ ; CONTINUE LIKE A READ 50$: MOVB R1,MEMORY(R2) ; PUT BYTE INTO VIRTUAL MEMORY 60$: INCR34 STEP ; Increment load address 65$: RETURN ; exit ;23MAR4 .PAGE .SBTTL READ INTEL FORMAT FILE ; :bbaaaattdddd...ddcc ; where: ; bb = data byte count ; aaaa = load address ; tt = block type: 00 = data, ; 01 = trailer, ;01APR5 ; 02 = extended address ;01APR5 ; 03 = transfer address ;01APR5 ; dd...dd = data bytes ; cc = checksum, -(bb+aa+aa+tt+dd+...+dd) RINTEL: CLRL CURSBA ; Default segment base address to 00000 ;01APR5 CLR -(SP) ; No transfer address yet ;01APR5 CLR -(SP) ; Will be DP if any ;01APR5 10$: CALL GETNOT ; Get a record, EOF is "no trailer" CLRB RECORD(R1) ; MAKE ASCIZ 30$: TSTB (R0) ; END OF RECORD? BEQ 10$ ; EQ: YES - GET ANOTHER CMPB #':,(R0)+ ; COLON FOUND? BNE 30$ ; NE: NO, KEEP LOOKING CLR CSUM ; Clear checksum CALL GETBYT ; GET DATA COUNT MOVB R1,R5 ; INITIALIZE DATA COUNT CALL GETWRD ; GET ADDRESS to %1 CALL DPADR ; Move to %3/%4, adding ADDVAL CALL GETBYT ; GET RECORD TYPE TSTB R1 ; See if zero BNE 215$ ; No, branch ;01APR5 TSTB R5 ; ZERO COUNT - MUST BE TRAILER RECORD BEQ 200$ ; EQ: PROCESS TRAILER RECORD ADD CURSBA,R3 ; NE: we have some data. Add segment base ;01APR5 ADC R4 ; to load address ;01APR5 ADD CURSBA+2,R4 ;01APR5 40$: ; PROCESS A DATA BYTE CALL GETBYT ; GET THE DATA BYTE CALL STOCMP ; Store or compare SOB R5,40$ ; CONTINUE FOR DATA COUNT 50$: CALL GETBYT ; GET CHECKSUM, SHOULD TOTAL 0 BEQ 10$ ; EQ: YES - PROCESS ANOTHER RECORD 210$: ; HANDLE BAD CHECKSUM JMP CSERROR ; CHECKSUM ERROR ; Process 01, 02, and 03 records ;01APR5 215$: DECB R1 ; See if 01 ;01APR5 BNE 230$ ; No, try extended address ;01APR5 200$: ; PROCESS TRAILER RECORD INCB TRAILER ; NOTE WE HAD ONE POP R3 ; Fetch 20-bit transfer address ;01APR5 POP R4 ; (or 00000) ;01APR5 CALL GETBYT ; GET THE CHECKSUM BNE 210$ ; EQ: O.K., NE, error RETURN ; Return 230$: DECB R1 ; Try extended address ;01APR5 BNE 240$ ; No, must be transfer address or error ;01APR5 CALL 300$ ; Yes, fetch SBA ;01APR5 MOV R3,CURSBA ; Save for future address updates ;01APR5 MOV R4,CURSBA+2 ;01APR5 BR 50$ ; Test checksum and return ;01APR5 ;01APR5 240$: DECB R1 ; Finally, must be 03, start address ;01APR5 BNE JITERROR ; "Invalid record type" if not ;01APR5 CALL 300$ ; OK, get CS ;01APR5 CALL GETWRD ; Fetch IP ;01APR5 INCR34 R1 ; Add together giving 20-bit transfer address ;01APR5 CALL ADDADV ; Include ADDVAL too ;01APR5 MOV R4,2(SP) ; Replace old transfer address ;01APR5 MOV R3,(SP) ; with new one ;01APR5 BR 50$ ; Test checksum and return ;01APR5 ;01APR5 ; Fetch SBA/CS, and DP shift left 4 bits into R3/R4, for 02 & 03 records ;01APR5 300$: CALL GETWRD ; Yes, fetch word = USBA ;01APR5 MOV R1,R3 ; Copy ;01APR5 .IF DF M$$EIS ;01APR5 CLR R2 ; Clear hi word ;01APR5 ASHC #4,R2 ; Shift up 4 bits ;01APR5 MOV R2,R4 ; Copy hi word to R4 ;01APR5 .IFF ;01APR5 CLR R4 ; Make it DP ;01APR5 .REPT 4 ; and shift up 4 bits ;01APR5 ASL R3 ;01APR5 ROL R4 ;01APR5 .ENDR ;01APR5 .ENDC ;01APR5 RETURN ; Done ;01APR5 .PAGE .SBTTL READ MOTOROLA FORMAT FILE ; Stbbaaaadddd...ddcc ; where: ; t = block type: 0 = header, 1 or 2 = data, 9 or 8 = EOF ; bb = byte count, including checksum and address ; aaaa = load address ; dd...dd = data bytes ; cc = checksum such that bb+aa+aa+tt+dd+...+dd+cc = $FF .ENABL LSB ;01APR5 RMOTOROLA: CALL GETNOT ; Get a record, EOF is "no trailer" CLRB RECORD(R1) ; Make ASCIZ 30$: TSTB (R0) ; End of record? BEQ RMOTOROLA ; EQ: yes - get another CMPB #'S,(R0)+ ; 'S' found? BNE 30$ ; NE: no, keep looking MOV #1,CSUM ; Preset checksum to give total of 0 MOVB (%0)+,%2 ; Get record type flag CALL GETBYT ; Get data count MOVB R1,R5 ; Initialize data count SUB #3,%5 ; Less address and c/sum BMI JITERROR ; Must be >=0 now. Invalid record if not ;01APR5 CLR %4 ; Clear address hi in case type 1 or 9 ; Records type 2 and 8 have 3-byte address. CMPB %2,#'2 ; Type 2? BNE 33$ ; No, try 8 DEC %2 ; Yes, process as type 1 from now on BR 34$ ; Get address hi 33$: CMPB %2,#'8 ; Check type 8 BNE 35$ ; No, should have normal 2-byte address INC %2 ; Else type 8 = type 9 (trailer) 34$: CALL GETBYT ; Get address bits 16-23 BISB %1,%4 ; to address hi DEC %5 ; Decrement count 35$: CALL GETWRD ; Get address lo MOV R1,R3 ; completing address in %3/%4 CALL ADDADV ; Add in ADDVAL CMPB %2,#'1 ; Data record? BEQ 37$ ; Yes, go process CMPB %2,#'0 ; Start record (0)? BEQ 300$ ; Yes, go process CMPB %2,#'9 ; Trailer (9)? BEQ 200$ JITERROR: ;01APR5 JMP ITERROR ; No, invalid record type 37$: ; 1: process a data byte TST %5 ; Make sure we have one BLE 60$ ; No, just checksum 40$: CALL GETBYT ; Get the data byte CALL STOCMP ; Store or compare SOB R5,40$ ; Continue for data count 60$: CALL GETBYT ; Get checksum, should total 0 BEQ RMOTOROLA ; EQ: yes - process another record 70$: ; Handle bad checksum JMP CSERROR ; Checksum error ; Trailer record, should be: S903aaaacc or S804aaaaaacc ; but Whitesmith's hex utility gives: S905aaaa0000cc, so skip any data bytes. 200$: ; Process trailer record INCB TRAILER ; Note we had one INC %5 ; Fetch any dummy data and checksum 210$: CALL GETBYT ; But don't do anything with the data SOB %5,210$ ; Real SOB leaves flags unchanged from csum GETBYT .IIF NDF M$$EIS, TSTB CSUM ; but simulated one destroys them BNE 70$ ; EQ: O.K., NE, error 220$: RETURN ; Return, SA already in %2 ; Header record: S0bbaaaannnn...nncc ; nn...nn is program NAM, hex-encoded ASCII 300$: TST %5 ; Any name given? BEQ 60$ ; No, just check the checksum MOV #RNM+RNMLEN-8.,%2 ; Address record name storage 310$: CALL GETBYT ; Fetch a name byte CMP %2,#RNM+RNMLEN ; Room to store it? BHIS 320$ ; Don't store if we have 8 already MOVB %1,(%2)+ ; Else put in new name area 320$: SOB %5,310$ ; Repeat for whole of name BR 60$ ; Check checksum and go for next record .DSABL LSB ;01APR5 .PAGE .SBTTL READ ROCKWELL FORMAT FILE ; ;bbaaaadddd...ddcccc ; where: ; bb = no of data bytes, dd...dd ; aaaa = start address ; cccc = checksum bb+aa+aa+dd+...+dd RROCKWELL: MOV ADDVAL,RECCNT ; Clear record counter MOV ADDVAL+2,RECCNT+2 ; allowing for ADDVAL offset 10$: CALL GETNOT ; Get a record, EOF is "no trailer" CLRB RECORD(R1) ; Make ASCIZ 30$: TSTB (R0) ; End of record? BEQ 10$ ; EQ: yes - get another CMPB #';,(R0)+ ; Semicolon found? BNE 30$ ; NE: no, keep looking CLR CSUM ; Clear checksum CALL GETBYT ; Get data count MOVB R1,R5 ; Initialize data count CALL GETWRD ; Get address, CALL DPADR ; including ADDVAL, to %3/%4 TSTB R5 ; Zero count - must be trailer record BEQ 200$ ; EQ: process trailer record ADD #1,RECCNT ; NE: data record, count it ADC RECCNT+2 40$: ; Process a data byte CALL GETBYT ; Get the data byte CALL STOCMP ; Store or compare SOB R5,40$ ; Continue for data count CALL 300$ ; Check checksum, return only if OK BR 10$ ; for another record 200$: ; Process trailer record INCB TRAILER ; Note we had one CALL 300$ ; Get and test the checksum CMP RECCNT,%3 ; "Address" is no of records in file, BNE 210$ ; excluding trailer record CMP RECCNT+2,%4 BEQ 220$ ; OK if matches no counted, else 210$: JMP BCERROR ; "Invalid block count" 220$: JMP NOXFER ; Return, no transfer address ; Get and check checksum. 300$: PUSH CSUM ; Push checksum so far CALL GETWRD ; Get checksum CMP %1,(SP)+ ; C.f. byte sum, should be same BEQ 320$ ; Yes - process another record JMP CSERROR ; No, checksum error 320$: RETURN ; Yes, return .PAGE .SBTTL READ RCA FORMAT FILE ; !Maaaa dd dd dd ...dd; ; where: ; aaaa = load address (1-n digits, only last 4 used) ; dd ... = data ; address ends on a non-hex character. Non-hex chars between data items are ; ignored. End of record should be one of: ; comma if next record contains data bytes contiguous with this ; (when no address field is given) ; semicolon if next record starts with new address (without !M) ; null if end of contiguous data. Next record must start ; !M or $P. ; ; RCATYP flag is set: ; =0 !M or !P expected (last record ended null) ; >0 record must start with address (last ended ;) ; <0 no address required (last ended ,) ; ; The transfer address is given in a record of the form: ; $Paaaa ; where aaaa is a 0-n character address (default 0). HEX assumes this to be ; the last thing in the file. RRCA: MOV #HEXDGT,DGTSUB ; Use hex conversion routine in GETNUM 5$: CLRB RCATYP ; !M or $P record required 10$: CALL GETNOT ; Get a record. EOF is "no trailer" CLRB RECORD(%1) ; Make ASCIZ TSTB RCATYP ; How should record start? BMI 40$ ; Data if -ve BNE 35$ ; Address if flag +ve, else: ; Line may start !M for load address, or $P for transfer address. 20$: MOVB (%0)+,%1 ; Fetch character BEQ 10$ ; Get another record if null CMPB %1,#'! ; Start of !M? BEQ 30$ ; Go see if it is CMPB %1,#'$ ; Start of $P? BNE 20$ ; No, try next char ; $Paaaa is EOF record, with transfer address. CMPB @%0,#'P ; Make sure it is $P BNE 20$ ; No, ignore $ INCB TRAILER ; Yes, trailer record CALL GETNUM ; Fetch transfer address (if any) MOV %1,%3 ; Copy to %3 CLR %4 ; Lo 4 digits only RETURN ; Return with it ; !Maaaa is load address. 30$: CMPB @%0,#'M ; Make sure it was !M BNE 20$ ; Treat ! as comment if not INC %0 ; Else address to follow, skip M 35$: CALL GETNUM ; Else get number up to terminator CALL DPADR ; Current address goes in %3/%4 ; Data values are 2-digit hex numbers. Any intervening non-hex chars are ; treated as comment, except that null, comma, and semicolon terminate a record,. 40$: MOVB (%0)+,%1 ; Fetch character BEQ 5$ ; Null is block terminator, cancelling address MOVB %1,RCATYP ; Set flag +ve for record ending ; CMPB %1,#'; ; Does it? BEQ 10$ ; EQ: yes - get another, with new address NEGB RCATYP ; Try for comma, which will signify data only CMPB %1,#', ; on next record BEQ 10$ SUB #'0,%1 ; Else try to convert to hex digit PUSH %3 ; Don't let HEXDGT change %3 JSR PC,HEXDGT ; Do we have one? POP %3 BCS 40$ ; No, ignore number separator DEC %0 ; Yes, point back to it CALL GETHX2 ; Get the data byte BCS 120$ ; Exit if error CALL STOCMP ; Store or compare BR 40$ ; Look for another, or end of record ; Error exit points 100$: JMP HCERROR ; "Conversion error" (junk after comma) 120$: JMP ERROR ; "Conversion error" (already reported by GETHX2 .PAGE .SBTTL READ STANDARD TEKTRONIX (TEKHEX) FORMAT FILE ; /aaaabbhhdddd...ddcc ; where: ; aaaa = start address ; bb = no of data bytes, dd...dd ; hh = header checksum a+a+a+a+b+b ; cc = data checksum d+d+...+d+d RTEKHEX: CALL GETNOT ; Get a record, EOF is "no trailer" CLRB RECORD(R1) ; Make ASCIZ 30$: TSTB (R0) ; End of record? BEQ RTEKHEX ; EQ: yes - get another CMPB #'/,(R0)+ ; Slash found? BNE 30$ ; NE: no, keep looking CMPB #'/,@%0 ; // is abort block BNE 35$ ; anything else should be data INCB TRAILER ; Note we had a trailer JMP NOXFER ; Abort block means no transfer address 35$: CLR CSUM ; Clear checksum CALL DIGWRD ; Get address CALL DPADR ; Current + ADDVAL address goes in %3/%4 CALL DIGBYT ; Get data count MOVB R1,R5 ; Initialize data count CALL GETCSM ; Get header checksum BNE 50$ ; Total should now be 0. Error if not TSTB R5 ; Zero count - must be trailer record BEQ 220$ ; EQ: all done 40$: ; Process a data byte CALL DIGBYT ; Get the data byte CALL STOCMP ; Store or compare SOB R5,40$ ; Continue for data count CALL GETCSM ; Check checksum BEQ RTEKHEX ; EQ: OK, go for another record 50$: JMP CSERROR ; NE: error 220$: INCB TRAILER ; Note we had a trailer RETURN ; Return, SA already in %3/%4 .PAGE .SBTTL READ EXTENDED TEKHEX RECORD ; %bbtccna...adddd...dd ; where: ; bb = no of data bytes, dd...dd ; t = block type, 6 = data, 3 = symbol, 8 = trailer ; cc = checksum b+b+t+n+a+...+a+d+d+...+d+d ; n = no of digits of load address ; a...a = start address, n digits ; d...d = data ; Equates for extended TekHex block types: DATA = '6 ; DATA RECORD SYMB = '3 ; SYMBOL TABLE RECORD TERM = '8 ; TERMINATION RECORD REXTENDED: CALL GETNOT ; Read record, EOF is "no trailer" CLRB RECORD(%1) ; Make it ASCIZ 5$: TSTB (R0) ; END OF RECORD? BEQ REXTENDED ; EQ: YES - GET ANOTHER CMPB #'/,@%0 ; / found? BNE 10$ ; No, try percent CMPB (%0)+,@%0 ; Yes, see if followed by another BNE 5$ ; Keep looking for % if not INCB TRAILER ; Call it a trailer JMP NOXFER ; // is an abort block without transfer address 10$: CMPB #'%,(R0)+ ; PERCENT FOUND? BNE 5$ ; NE: NO, KEEP LOOKING CALL CHECK ; VERIFY BLOCK SIZE AND CHECKSUM CMPB #DATA,(R0) ; IS THIS A DATA RECORD? BEQ 34$ ; EQ: YES, PROCESS A DATA RECORD CMPB #SYMB,(R0) ; IS THIS A SYMBOL TABLE RECORD? BEQ 100$ ; Yes, go get name CMPB #TERM,(R0) ; IS THIS A TRAILER RECORD? BEQ 200$ ; EQ: PROCESS TRAILER RECORD JMP ITERROR ; NE: "INVALID BLOCK TYPE" 34$: ADD #3.,R0 ; SKIP DATA TYPE AND CHECKSUM (ALREADY CHECKED) CALL GETHXV ; GET VARIABLE LENGTH HEX LOAD ADDRESS ; INTO R1 (LOW WORD) AND R2 (HIGH WORD) BCS 250$ ; CS: TAKE ERROR EXIT MOVB BLKLEN,R5 ; COMPUTE NUMBER OF DATA BLOCKS SUB R3,R5 ; SUBTRACT LENGTH OF VARIABLE FIELD SUB #5,R5 ; SUBTRACT BLK CNT, CHKSUM, TYPE FIELD ASR R5 ; DIVIDE BY TWO TO GIVE NUMBER OF DATA PAIRS BCC 38$ ; CC: EVEN DATA PAIRS JMP BCERROR ; CS: "INVALID BLOCK COUNT" 38$: MOV R1,R3 ; PUT LOAD ADDRESS INTO R3 (LOW WORD) MOV R2,R4 ; PUT LOAD ADDRESS INTO R4 (HIGH WORD) CALL ADDADV ; + ADDVAL TST R5 ; ANY PAIRS TO PROCESS? BEQ REXTENDED ; EQ: NONE THIS RECORD 40$: CALL GETHX2 ; GET THE DATA BYTE BCS 250$ ; CS: TAKE ERROR EXIT CALL STOCMP ; Store or compare 65$: SOB R5,40$ ; DECREMENT NUMBER OF PAIRS TO READ ;23MAR4 BR REXTENDED ; Get another record 100$: ; Symbol table entry. Just get program name MOV #RNM+RNMLEN-8.,%2 ; Point to area for name read CMPB @%2,#SPACE ; Do we have one already? BNE REXTENDED ; Yes, don't get another ADD #3,%0 ; Point to name field CALL GETHX1 ; Get length BCS 250$ ; Trap conversion error MOVB %1,%1 ; Sign extend byte BEQ REXTENDED ; Null name, get another record 110$: MOVB (%0)+,(%2)+ ; Copy name from record CMP %2,#RNM+RNMLEN ; Until end of storage area BHI REXTENDED SOB %1,110$ BR REXTENDED ; Or all done 200$: ; Trailer: INCB TRAILER ; Note we had one ADD #3.,R0 ; SKIP DATA TYPE AND CHECKSUM (ALREADY CHECKED) CALL GETHXV ; GET VARIABLE LENGTH HEX TRANSFER ADDRESS BCS 250$ ; CS: ERROR MOV R1,R3 ; RETURN TRANSFER ADDRESS IN R3/R4 MOV R2,R4 RETURN 250$: JMP ERROR ; Error exit (abnormal) .PAGE .SBTTL CHECK - CHECK FOR VALID EXTENDED TEKHEX FORMAT CHECK: JSR %5,.SAVR1 ; Save registers %1-%5 CLR R4 ; ZERO R4 FOR CHECK SUM ACCUMULATION MOV R0,R5 ; SAVE START ADDR OF STRING MOVB (R0)+,R1 ; START CHECKING RECORD BEQ LENERR ; INVALID LENGTH ? GET OUT CALL CKSUM ; START CHECK SUM MOVB (R0)+,R1 ; GET NEXT CHAR BEQ LENERR ; INVALID LENGTH ? GET OUT CALL CKSUM ; VALIDATE & ADJUST CHECK SUM MOVB (R0)+,R1 ; CHECK OUT BLOCK TYPE BEQ LENERR ; NULL BYTE ? INVALID LENGTH CALL CKSUM ; VALIDATE & ADJUST CHECK SUM CALL GETHX2 ; OBTAIN CHECK SUM VALUE BCS ERRSET ; IF ERROR, GET OUT MOVB R1,R3 ; SAVE GIVEN CHECK SUM IN R3 5$: MOVB (R0)+,R1 ; VALIDATE THE REST OF RECORD BEQ FINISH ; END OF LINE ? FINISH UP CALL CKSUM ; VALIDATE & ADJUST CHECK SUM BR 5$ ; PROCESS THE NEXT BYTE FINISH: CMPB R3,R4 ; CHECK SUM RIGHT ? BNE CKSERR ; CHECK SUM ERROR ? GET OUT DEC R0 ; ADJUST POINTER BACK TO NULL SUB R5,R0 ; OBTAIN ACTUAL LENGTH OF STRING MOV R0,R3 ; SAVE LENGTH IN R3 MOV R5,R0 ; PUT START ADDR BACK IN R0 CALL GETHX2 ; GET BLOCK LENGTH FROM STRING BCS ERRSET MOVB R1,BLKLEN ; SAVE BLOCK LENGTH FOR POSTERITY BCS ERRSET ; LENGTH ERROR ? GET OUT CMPB R1,R3 ; VALIDATE LENGTH BEQ QUIT ; OK, return LENERR: JMP BCERROR ; INCORRECT BLOCK COUNT CKSERR: JMP CSERROR ; CHECK SUM ERROR ERRSET: JMP ERROR ; Hex conversion error QUIT: RETURN ; Return (only if) all OK, co-routine pops regs CKSUM: CMPB #'0,R1 ; COMPARE LOW BYTE OF R1 WITH '0' BGT SPCLOW ; CHECK TO SEE IF BYTE IS A '$','%','.', OR '_' SUB #'0,R1 ; COMMENCE TO CONVERTING CMPB #9.,R1 ; CHECK RANGE 0. THRU 9. BGE OK ; GOOD TEK HEX CHARACTER SUB #7.,R1 ; TAKE A SECOND CONVERSION STEP CMPB #10.,R1 ; CHECK FOR 'A' (UPPER CASE) BGT BAD ; NOT VALID TEK HEX CMPB #35.,R1 ; CHECK FOR 'Z' (UPPER CASE) BGE OK ; VALID BETWEEN 'A' AND 'Z' SUB #2.,R1 ; ADJUST FOR LOWER CASE CMPB #40.,R1 ; CHECK FOR 'a' (LOWER CASE) BGT SPCHI ; CHECK FOR '_' (UNDERLINE) CMPB #65.,R1 ; CHECK FOR 'z' (LOWER CASE) BGE OK ; VALID BETWEEN 'a' THRU 'z' (LOWER CASE) BR BAD ; NOT VALID TEK HEX SPCLOW: CMPB #'$,R1 ; CHECK FOR '$' BEQ OK ; $ IS VALID TEK HEX CMPB #'%,R1 ; CHECK FOR '%' BEQ OK ; % IF VALID TEK HEX SUB #8.,R1 ; ADJUST TO CHECK '.' (PERIOD) CMPB #38.,R1 ; CHECK FOR '.' BEQ OK ; '.' IS VALID TEK HEX BR BAD ; NOT VALID TEK HEX SPCHI: INC R1 ; ADJUST TO CHECK '_' CMPB #39.,R1 ; CHECK FOR '_' (UNDERSCORE) BEQ OK ; '_' IS VALID TEK HEX BAD: OUTPUT BET ; BAD CHAR IN EXTENDED TEK HEX JMP ERROR ; GET OUT OK: ADD R1,R4 ; ADD TO CHECK SUM RETURN .PAGE .SBTTL READ TEXAS FORMAT FILE ; tddddtddddtdddd... ; where: ; t = record type ("tag character"): ; 0 = program name (0aaaannnnnnnn) ; 1 = start address ; 7 = checksum, -(sum of ASCII chars since last csum) ; 9 = load address ; B = data ; F = end of record (no dddd) ; : = end of file ; others are defined, but not supported by HEX ; dddd = address or data, always a full word. RTEXAS: CLR -(SP) ; Clear start address CLR %3 ; Clear load address CLR %4 1$: CALL GETREC ; Get a record 220$ ; Don't worry if ':' trailer missing CLRB RECORD(R1) ; Make ASCIZ CLR CSUM ; Clear checksum 30$: MOVB (R0)+,%2 ; Get tag character, null = end of record BEQ 1$ ; EQ: yes - get another CMPB %2,#'F ; 'F' is end of record too BEQ 1$ CMPB %2,#': ; Colon found? BEQ 220$ ; Yes, end of file ADD %2,CSUM ; Add tag character to checksum CALL GETHX4 ; All other items have address/data -- get it to %1 BCC 40$ ; Make sure it worked JMP ERROR ; Else error 40$: CMPB %2,#'7 ; Checksum fetch? BNE 50$ ; No, branch ADD %1,CSUM ; Yes, sum + value read should give 0 BEQ 30$ ; Go get next item if it does JMP CSERROR ; Else checksum error ; All other types require ASCII char codes to be added to checksum. 50$: MOV #4,%5 ; Load a byte count SUB %5,%0 ; Step back over address 60$: CLR -(SP) ; Clear hi byte of @SP MOVB (%0)+,@SP ; Get a digit to lo ADD (SP)+,CSUM ; Add it to checksum SOB %5,60$ ; Repeat until 0 CMPB %2,#'0 ; 0 is program name specification BEQ 80$ ; Go process it CMPB %2,#'B ; B = data? BNE 70$ ; No, branch CALL STHCMP ; Store/compare hi CALL STHCMP ; Then lo BR 30$ ; Go back for next item 70$: CMPB %2,#'9 ; 9 = load address? BNE 90$ ; No, only 1 is left CALL DPADR ; Yes, copy address + ADDVAL to %3/%4 BR 30$ ; and get next item ; 0 is program name, 8 ASCII chars. 80$: MOV #8.,%2 ; Load counter MOV #RNM+RNMLEN-8.,%1 ; Load name pointer 85$: CLR -(SP) ; Get a character MOVB (%0)+,@SP ; as a word on the stack ADD @SP,CSUM ; Add it to checksum, MOVB (SP)+,(%1)+ ; store it and purge stack SOB %2,85$ ; Repeat for 8 chars BR 30$ ; and get another item 90$: CMPB %2,#'1 ; Only thing left is start address BEQ 100$ ; OK if that's it JMP ITERROR ; Else illegal block type 100$: MOV %1,@SP ; Store start address for exit BR 30$ ; Go get another item 220$: ; Trailer record INCB TRAILER ; Note we had one POP %1 ; Fetch SA from stack CALLR DPADR ; Add ADDVAL and return .PAGE .SBTTL MOSTEK FORMAT ; Four record types: ; F0rrrrssnn...nnaaiimmllllhhhhcc (module header) ; rrrr is record length ss...cc ; ss is length of module name (0-100) ; nn...nn hex-encoded ASCII program name ; aa address size, 16- or 32-bits ; ii processor ID (ignored) ; mm module type, 0 or 2 = no xfer addr, 1 or 3 = xfer addr ; llll lowest address in module (ignored) ; hhhh highest address in module (ignored). ; cc checksum, s.t. rr+rr+...+cc = 0. ; ; F2rrrraa...aadddd...ddcc (enumerated data) ; aa...aa 32- or 16-bit load address ; dd...dd data bytes ; ; F4rrrraa...aabbbb...bbcc (iterated data) ; bb...bb replicated data blocks ; This block type is not currently supported. ; ; F6rrrraa...aacc (end record) ; aa...aa is transfer address ; Equates for Mostek record types: MSHEAD = '0 ; MODULE HEADER RECORD MSENUM = '2 ; ENUMERATED DATA RECORD MSITER = '4 ; ITERATED HEADER RECORD MSTERM = '6 ; MODULE END RECORD RMOSTEK: CALL GETNOT ; Get a record, EOF is "no trailer" CLRB RECORD(%1) ; Make it ASCIZ 1$: TSTB (R0) ; END OF RECORD? BEQ RMOSTEK ; EQ: YES - GET ANOTHER RECORD CMPB #'F,(R0)+ ; "F" FOUND? BNE 1$ ; NE: NO, KEEP LOOKING CALL CHECKM ; VERIFY MOSTEK CHECKSUM CMPB #MSHEAD,(R0) ; IS THIS A MOSTEK HEADER RECORD? BEQ 10$ ; EQ: YES, PROCESS A MOSTEK HEADER RECORD CMPB #MSENUM,(R0) ; IS THIS AN ENUMERATED DATA RECORD? BEQ 20$ ; EQ: YES, PROCESS AN ENUMERATED RECORD CMPB #MSITER,(R0) ; IS THIS AN ITERATED DATA RECORD? BEQ 100$ ; EQ: YES, PROCESS AN ITERATED RECORD CMPB #MSTERM,(R0) ; IS THIS A TRAILER RECORD? BEQ 200$ ; EQ: PROCESS TRAILER RECORD JMP ITERROR ; "INVALID BLOCK TYPE" 10$: ; PROCESS MOSTEK HEADER RECORD ADD #5,R0 ; SKIP TYPE AND BLOCK LENGTH CALL GETHX2 ; GET RECORD LENGTH (# OF BYTES) IN R1 BCS 250$ MOVB %1,-(SP) ; Push as counter BEQ 13$ ; Nothing to do if zero length MOV #RNM+RNMLEN-8.,%2 ; Address record name storage 11$: CALL GETHX2 ; Fetch a name byte BCS 250$ CMP %2,#RNM+RNMLEN ; Room to store it? BHIS 12$ ; Don't store if we have 8 already MOVB %1,(%2)+ ; Else put in new name area 12$: DECB @SP ; Repeat for whole of name BNE 11$ 13$: POP ; Purge stack CALL GETHX2 ; GET ADDRESS SIZE BCS 250$ MOVB R1,MOSTYP ; STORE ADDRESS MODE CMPB #16.,R1 ; IS IT 16-BIT? BEQ 15$ ; EQ: O.K. CMPB #32.,R1 ; IS IT 32-BIT? BEQ 15$ ; EQ: O.K. OUTPUT IAS ; "Invalid address size" BR 250$ ; TAKE ERROR EXIT 15$: CALL GETHX4 ; GET PROCESSOR TYPE (WHO CARES?) ; AND MODULE TYPE BCS 250$ ; CS: HEX CONVERSION ERROR ROR R1 ; IS TRAILER RECORD ON THIS MODULE? BCS RMOSTEK ; CS: YES INCB PART ; FORCE PARTIAL READ BR RMOSTEK ; Get next record 20$: ; PROCESS ENUMERATED RECORD CALL 210$ ; Get address and data count TST R5 ; ANY MORE PAIRS TO PROCESS? BEQ RMOSTEK ; EQ: NONE LEFT THIS RECORD 40$: CALL GETHX2 ; GET THE DATA BYTE BCS 250$ ; CS: TAKE ERROR EXIT CALL STOCMP ; Store or compare SOB R5,40$ ; DECREMENT NUMBER OF PAIRS TO READ BR RMOSTEK ; ALL DONE THIS RECORD 100$: ; Process iterated record CALL 210$ ; Get address CALL 1000$ ; Process iterated data block (recursively) BR RMOSTEK ; Go get another record 200$: ; Trailer -- get transfer address INCB TRAILER ; Note we had one 210$: ; Also gen purpose get address routine MOVB BLKLEN,%5 ; Keep length of block in %5 CLR %1 ; Clear address lo CLR %2 ; and hi CMP %5,#1 ; If block length is 1 BLE 207$ ; Have checksum only (trailer blk w/o xfer addr) ADD #5,R0 ; SKIP REC TYPE AND REC LENGTH CMPB #16.,MOSTYP ; 16-BIT MODE? BEQ 202$ ; EQ: YES - GET 4 HEX DIGITS CMPB #32.,MOSTYP ; 32-BIT MODE? BEQ 204$ ; EQ: YES - GET 8 HEX DIGITS 249$: OUTPUT HRR ; "Header record required" 250$: JMP ERROR ; TAKE ERROR EXIT 202$: CALL GETHX4 ; GET 4 DIGIT TRANSFER ADDRESS BCS 250$ BR 206$ 204$: CALL GETHX8 ; GET 8 DIGIT TRANSFER ADDRESS BCS 250$ SUB #2,R5 ; ADJUST BLOCK LENGTH FOR LATER COMPUTATION 206$: SUB #2,R5 ; Compute data count by adjusting block length 207$: DEC %5 ; by address and checksum count MOV R1,R3 ; RETURN ADDRESS IN R3/R4 MOV R2,R4 CALLR ADDADV ; Add in ADDVAL and return ; Get iterated data record, recursively if necessary. Iterated block format is: ; mmmmbbccc... ; where ; mmmm is repeat count ; bb = no of embedded blocks, 0 if none ; ccc... is another iterated block if bb <> 0, else: ; nndd...dd ; where ; nn is byte count of dd...dd data pairs 1000$: PUSH %5 ; Save outer repeat count CALL GETHX4 ; Get repeat count for this block BCS 250$ MOV %1,%5 ; Copy repeat count CALL GETHX2 ; Get inner block flag BCS 250$ TSTB %1 ; Any more? BEQ 1020$ ; No, this is the last 1010$: PUSH %0 ; Yes, save where we are CALL 1000$ ; Call self for inner one(s) POP %0 ; Restore pointer SOB %5,1010$ ; Repeat mmmm times BR 1040$ ; and exit 1020$: CALL GETHX2 ; Final inner block, get byte count BCS 250$ CLR %5 ; to %5 BISB %1,%5 ; w/o sign extend 1030$: CALL GETHX2 ; Get data byte BCS 250$ CALL STOCMP ; Store or compare SOB %5,1030$ ; Repeat until done 1040$: POP %5 ; When restore outer count RETURN ; and return to caller (or self) .PAGE .SBTTL CHECKM - CHECK MOSTEK RECORD CHECKSUM CHECKM: PUSH %0 ; SAVE R0-R2 PUSH %1 PUSH %2 DEC R0 ; POINT BACK AT FIRST CHARACTER CLR CSUM ; Clear checksum CALL GETBYT ; Get record type into it CALL GETWRD ; GET RECORD LENGTH MOVB R1,BLKLEN ; SAVE BLOCK LENGTH FOR LATER CALCULATIONS MOVB R1,R2 ; HOW MANY "BYTES" LEFT THIS RECORD? BEQ 40$ ; EQ: THAT'S A BLOCK LENGTH ERROR 10$: CALL GETBYT ; Get the next data pair to CSUM SOB R2,10$ ; CONTINUE FOR ALL DATA BYTES 20$: .IIF NDF M$$EIS, TSTB CSUM ; CHECKSUM SHOULD NOW BE 00 BEQ 30$ ; EQ: CHECKSUM IS O.K. JMP CSERROR ; "Checksum error" 30$: TSTB (R0) ; WE SHOULD NOW BE AT THE END OF THE RECORD BEQ 255$ ; EQ: WE ARE 40$: JMP BCERROR ; "Invalid block count" 255$: POP %2 ; Restore %2-%0 POP %1 POP %0 RETURN ; RESTORE REGS .PAGE .SBTTL READ PDP-8/IM6100 OBJECT FILES ; File consists of binary records of byte pairs in the form: ; tthhhhhh 00llllll ; where: ; tt is the record type: 10 = leader (ignored) ; 01 = hhhhhhllllll is address ; 00 = hhhhhhllllll is data ; ; Two actual types are supported in the same READ module: ; RIM ("Read In Mode") is the simple (bootstrap) loader format, ; where address and data bytes alternate. ; BIN does not require addresses to be specified where data are ; contiguous. ; HEX differentiates bewteen the two types only for WRITE. ; 12-bit data bytes are stored with the hi 4 bits in the first 8-bit byte, ; and the lo 8 bits in the second. Thus the HEX address increments by 2 for ; each PDP-8 12-bit word, and is twice the value given in the file (including ; ADDVAL). RRIM: RBIN: CLR SAVLEN ; Clear length to force a read CLR %1 ; Default load address is ADDVAL 10$: CALL DPADR ASL %3 ; Addresses are always twice ROL %4 ; value given 1$: CALL 200$ ; Get a 12-bit value BCS 10$ ; Carry set is address, clear is data CALL STHCMP ; Store or compare hi byte CALL STHCMP ; Repeat for lo BR 1$ ; Go get next thing 90$: CALL GETEND ; Get a record; EOF is end, no transfer address 100$: DEC SAVLEN ; Else see if a byte to fetch BMI 90$ ; No, get another record MOVB (%0)+,%1 ; Fetch byte BMI 100$ ; Bit 8 set is leader/trailer, get another RETURN ; Else return with it ; Get a word to %1. Return with carry clear if data, set if address. 200$: CALL 100$ ; Get hi byte PUSH %1 ; to stack CALL 100$ ; Get lo BIT #^B11000000,%1 ; Bits 6 (& 7) must be clear BEQ 210$ JMP HCERROR ; Conversion error if not 210$: SWAB @SP ; Swap stack bytes ASR @SP ; Shift right twice ASR @SP ; (clears carry, since lo byte is 0) BIS (SP)+,%1 ; Form 12-bit value BIT #^B1000000000000,%1 ; If bit 13 is clear BEQ 220$ ; This is data SEC ; Else set carry to flag address 220$: BIC #^B1111000000000000,%1 ; Clear bits 12-15 anyway RETURN .PAGE .SBTTL WHITESMITHS' V2.1 LINKER FORMAT (XEQ. FILE) ; File consists of single-byte, fixed-length records, as: ; ; byte 1: ident byte, always 99H ; byte 2: configuration byte: ; bits 0-2: (no of chars in symbol table field)/2 - 1 ; bit3: 0 = ints are 2 bytes, 1 = ints are 4 bytes ; bit4: 0 = ints stored msb first, 1 = lsb first ; bits 5-6: storage bound restriction control bits ; bit 7: 0 = relocation information supplied, 1 = not supp ; bytes 3-4: size of symbol table ; Next 6 entries are 2 or 4 byte ints according to setting of bit3 of byte 2: ; int 1: number of text (program code) bytes ; int 2: number of data bytes ; int 3: no of bss bytes (uninitialised data) ; int 4: size of stack+heap ; int 5: text area start address ; int 6: data area start address ; This is followed by four contiguous segments of lengths controlled by the above: ; segment 1: text area ; segment 2: data area ; segment 3: symbol table ; segment 4: relocation information ; ; HEX only reads the text and data areas, and assumes that all relocation has ; already been done by the Whitesmiths' linker. Whitesmiths' object files, ; which have the same format, may thus be read, but relocation will not have ; been done. ; ; CAUTION: Whitesmiths set the file record size to 1, but write to every byte ; in the file, whereas since FCS forces fixed length records to start at even ; addresses, it only reads every other byte in these circumstances. Since we ; are in locate mode, it is quite possible to read the gaps in the same way, ; but this may not work on later releases of RSX-11M (OK for V4.0). See also ; WRITE. RWHITESMITHS: CALL GETNOT ; Get first byte pair CMPB (%0)+,#231 ; 99H? BEQ 10$ ; Yes, OK JMP ITERROR ; No, not a Whitesmith's format file 10$: MOVB @%0,CONFIG ; OK, get configuration byte CALL GETNOT ; Skip symbol table size (2 bytes) CALL 200$ ; Get text area size MOV %0,%2 ; Save hi in %2 MOV %1,%5 ; and lo in %5 CALL 200$ ; Data area size PUSH %1 ; Save for later PUSH %0 CALL 200$ ; Skip bss CALL 200$ ; and stack+heap sizes CALL 200$ ; Get text start address MOV %1,%3 ; To %3 (lo) MOV %0,%4 ; and %4 (hi) CALL 200$ ; Then data start address PUSH %1 ; Save that for later PUSH %0 CLR R0 ; Want a new record ;01SEP6 CALL 50$ ; Read text POP %4 ; Get data area start address POP %3 POP %2 ; and byte count hi POP %5 ; and lo ; CALLR 50$ ; Load data and return 50$: ; Read %2/%5 bytes to area addressed by %3/%4 PUSH %2 ; Save count hi BIS %5,%2 ; Make sure there is something to do BEQ 70$ ; Exit if not CALL ADDADV ; Add ADDVAL to address 60$: BIT #1,%0 ; OK, get a byte. If %0 is at an odd address, BNE 65$ ; read it from the inter-record gap(!) CALL GETNOT ; Else get a new record 65$: MOVB (%0)+,%1 ; Copy byte to %1 CALL STOCMP ; Store/compare SOB %5,60$ ; Repeat for count bytes DEC @SP ; in double-precision BPL 60$ 70$: CLR %3 ; No start address ;22SEP6 CLR %4 POP ; Purge stack of count hi RETURN ; Read an int value in no of bytes and order defined in configuration byte, ; CONFIG. Return value in %0(hi), and %1(lo). 200$: CALL 300$ ; Get first two bytes CLR %0 ; Clear %0 BIT #^B1000,CONFIG ; in case 16-bits only BEQ 220$ ; Return if so PUSH %1 ; Else 2 more bytes to come. Save first 2 CALL 300$ ; Fetch next two ; Now have a long int in @SP (1st 2 bytes read), and both %0 and %1 (second 2). ; If CONFIG bit 4 is clear, 1st two read go into hi=%0, lo=%1 is second 2. ; If CONFIG bit 4 is set, 1st two read go into lo=%1, second two into hi=%0 ; right from 300$, but word order must be reversed if CONFIG bit 4 is set. BIT #^B10000,CONFIG ; Which byte first? BEQ 210$ ; Clear = msb, just pop 1st pair to %0 MOV %1,%0 ; Set = lsb, copy hi just read POP %1 ; Fetch lo read first BR 220$ ; and return 210$: POP %0 ; Get high word to %0 220$: RETURN ; Get a 2-byte short int to %1, in byte order according to CONFIG. 300$: CALL GETNOT ; Get a byte pair MOV (%0)+,%1 ; to %1 BIT #^B10000,CONFIG ; Storing lsb first? BNE 310$ ; Yes if bit set, same as PDP-11 SWAB %1 ; No, swap bytes first 310$: RETURN .PAGE .SBTTL HEX-CHAR AND OCTAL-CHAR FORMATS ; ^B ; $Abbbb, ; aaaa-ddxddxddx...ddx ; aaaa-ddxddxddx...ddx ; ^C ; $Sssss, ; where: ; ctrl/B marks start of data ; bbbb is load address for Data I/O programmer ; aaaa is alternative load address (see below) ; dd is data ; s is separator, in SEPTOR, usually space, ', or % ; ctrl/C marks end of data ; ssss is checksum = dd+dd+... ; ; Formally, bbbb is the true address, and aaaa's should be ignored. However, to ; cater for early Data I/O programmers which can't relocate, aaaa- may be ; included to override bbbb. (If x is a hyphen, use aaaa= instead). ; ; The no of digits in the data items is not fixed -- only the last byte before ; the separator is significant, and anything else intervening is ignored. .ENABL LSB RHEX: MOV #HEXDGT,DGTSUB ; Load hex digit test routine addr BR 5$ ; Join common code with octal ROCTAL: MOV #OCTDGT,DGTSUB ; Load routine to test for octal digit ; Look for ^B, defining start of data. 5$: CLR %1 ; Default store address is 0 CALL DPADR ; + ADDVAL CLR CSUM ; Clear checksum MOVB #'-!200,ADREND ; Address at start of line will end '-' CMPB SEPTOR,#'- ; Unless that's the data separator BNE 10$ MOVB #'=!200,ADREND ; When use '=' instead ; Look for start of data, end of file, or checksum ($Saaaa,) for data just ; read in (if any). 10$: CALL GETEND ; Get a record; end of file is trailer record CLRB RECORD(R1) ; Make ASCIZ 20$: TSTB @%0 ; End of record? BEQ 10$ ; Yes, get another CMPB @%0,BEGFLG ; Begin flag? BEQ 25$ ; Yes, go get it CMPB BEGFLG,#'B&37 ; If begin flag control-B (i.e. not SMS) BNE 30$ CMPB @%0,#'A&37 ; then control-A is also a begin flag BNE 30$ ; No, try checksum 25$: CLR CSUM ; Yes, clear new sum BR 140$ ; for data items following ; Only other acceptable thing after a trailer is checksum, as $Saaaa. 30$: MOV #'S,%1 ; Check for '$S' CALL 300$ BCS 20$ ; Ignore anything else SUB %1,CSUM ; Checksum OK? Reset to 0 if so BEQ 20$ ; Yes, go look for another block JMP CSERROR ; No, checksum error ; Data read section. Get and store bytes, checking terminator:- ; ENDFLG means end of data block ; SEPTOR means preceding thing was a data item, ; ADREND means preceding number was an address, ; $A means following number is an address, terminated by comma. 130$: CALL GETNOT ; Get a record, shouldn't end without trailer now BICB #200,ADREND ; Allow recognition of special address flag CLRB RECORD(R1) ; Make ASCIZ BR 142$ ; Check first byte 140$: BISB #200,ADREND ; Don't recognise address in middle of line 141$: INC %0 ; Bypass number terminator 142$: TSTB @%0 ; End of record? BEQ 130$ ; Yes, get another CALL GETNUM ; Fetch {number} and terminator CMPB @%0,SEPTOR ; Data item? BNE 150$ ; No, branch BIC #^C377,%1 ; Yes, get lo byte only ADD %1,CSUM ; Add to checksum CALL STOCMP ; Load/compare data BR 140$ ; Go get more 150$: CMPB @%0,ADREND ; New address? BEQ 160$ ; Yes, go set it CMPB @%0,ENDFLG ; No, end of data flag? BEQ 10$ ; Yes, trailer MOV #'A,%1 ; Only other thing is 'A' CALL 300$ BCS 142$ ; No, ignore as comment 160$: CALL DPADR ; New address, load it to %3/%4 ADD %2,%4 ; including hi word BR 140$ ; Get next thing ; Check for control record: $xaaaa, where x is in %1. ; Ignore if x is not expected character, else fetch aaaa, making sure it ; ends with a comma. Return cc if value fetched, cs if not expected $x. 300$: CMPB (%0)+,#'$ ; Dollar? BNE 310$ ; No, skip character CMPB (%0)+,%1 ; Yes, check letter -- A or S BNE 310$ ; Ignore any other combination CALL GETNUM ; Get address or checksum CMPB @%0,#', ; Should have ended on comma BNE 320$ ; Error if not TST (PC)+ ; Clear carry, skipping SEC 310$: SEC ; Come here to set carry if not expected characters RETURN ; Return 320$: JMP HCERROR ; Else invalid number format .DSABL LSB .IF DF TCI .PAGE .SBTTL TCI FORMAT ; ; @aaaadddd...dd ; where: ; aaaa = address ; dd...dd is data bytes RTCI: CALL GETEND ; Get a record; end of file is trailer record MOV R1,R5 ; SAVE LRECL CLRB RECORD(R1) ; MAKE ASCIZ 30$: TSTB (R0) ; END OF RECORD? BEQ RTCI ; EQ: YES - GET ANOTHER DEC R5 ; DECREMENT LRECL CMPB #'@,(R0)+ ; @ FOUND? BNE 30$ ; NO - KEEP LOOKING SUB #4,R5 ; COMPUTE DATA COUNT ASR R5 ; NUMBER OF BYTES LEFT /2 CALL GETWRD ; GET ADDRESS CALL DPADR ; CURRENT ADDRESS GOES IN R3/R4 40$: ; PROCESS A DATA BYTE CALL GETBYT ; GET THE DATA BYTE CALL STOCMP ; Store or compare SOB R5,40$ ; CONTINUE FOR DATA COUNT BR RTCI ; PROCESS ANOTHER RECORD .ENDC ; TCI format support .PAGE .SBTTL FAIRCHILD FAIRBUG FORMAT ; Saaaa ; Xddddddddddddddc ; * ; where: ; aaaa = address ; dd...dd = data bytes ; c = checksum, lo nybble of sum of data byte nybbles RFAIRCHILD: CALL GETNOT ; Get a record; end of file is "No trailer" MOV R1,R5 ; Save LRECL CLRB RECORD(R1) ; Make ASCIZ 10$: TSTB (R0) ; End of record? BEQ RFAIRCHILD ; EQ: yes - get another DEC R5 ; Decrement LRECL CMPB #'X,(R0)+ ; X found? BEQ 30$ ; Yes, go get data record CMPB #'*,-(%0) ; * found? BNE 20$ ; No, try S INCB TRAILER ; Yes, trailer record JMP NOXFER ; Done, no transfer address 20$: CMPB (%0)+,#'S ; S found? BNE 10$ ; No -- keep looking CALL GETWRD ; Get address CALL DPADR ; Current address goes in R3/R4 BR RFAIRCHILD ; Start a new record ; Get (%5-1)/2 data bytes. %5 should be odd, so no need to subtract 1. 30$: ASR R5 ; Data count = (number of bytes left -1) /2 CLR CSUM ; Clear record checksum 40$: CALL DIGBYT ; Get the data byte CALL STOCMP ; Store or compare SOB R5,40$ ; Continue for data count CALL GETHX1 ; Get final nybble = checksum SUB CSUM,%1 ; C.f. total of hex data nybbles BIT #^B1111,%1 ; (lo nybble only) should be 0 BEQ RFAIRCHILD ; OK, process another record JMP CSERROR ; Else "Checksum error" .PAGE .SBTTL SIRA BINARY FORMAT ; 1aatbbdddd...ddc ; where (bytes): ; aa = address -- lo/hi ; t = type: 0=data, 1=EOF, 2=autostart ; bb = byte count -- lo/hi ; dd...dd = data bytes ; c = checksum, a+a+t+b+b+d+...d RSIRA: CLR SAVLEN ; Say no data, so first BINBYT will fetch it ; Look for an 001 byte, signifying start of record. 10$: CALL BINBYT ; Get byte DEC %1 ; Is it 1? BNE 10$ ; No, keep looking CLR CSUM ; Found start, don't include in checksum JSR PC,BINWRD ; Next word = store address CALL DPADR ; Copy address + ADDVAL JSR PC,BINBYT ; Get next byte = block type MOV %1,%5 ; Save JSR PC,BINWRD ; While reading byte count TSTB %5 ; Test type BNE 50$ ; Branch if EOF block MOV %1,%5 ; Else this is a program block, save count BEQ 30$ ; Trap dummy null record ; Read and store the %5 bytes in the record, where %3/%4 points. 20$: JSR PC,BINBYT ; Read byte JSR PC,STOCMP ; Store byte, advancing memory pointer SOB %5,20$ ; Decrement count and repeat until all done ; Make sure checksum is right. 30$: CALL BINCSM ; Fetch and test checksum BEQ 10$ ; Get another record if OK 40$: JMP CSERROR ; Else binary checksum error ; End-of-file block. If type (%5) = 2, have autostart. In any case, start ; address (or 0) is in %2. 50$: INCB TRAILER ; Note we had a trailer TST %1 ; Byte count should be 0 BEQ 60$ ; OK if so JMP BCERROR ; "Invalid block count" if not 60$: CMP %5,#2 ; Type =1 or 2? BLOS 70$ ; Yes, just check checksum JMP ITERROR ; No, say "Invalid block type" 70$: CALL BINCSM ; Final byte is checksum BNE 40$ ; Else error RETURN ; OK if so .PAGE .SBTTL MACRO-11 ABSOLUTE BINARY FORMAT ; Assemble MACRO source with .ENABL ABS, or /EN:ABS. ; ; aadd... ; where: ; aa = load/start address ; d... = data bytes .ENABL LSB ; Read and store the %5 bytes in the record, where %3/%4 points. 10$: MOVB (%0)+,%1 ; Get next byte CALL STOCMP ; Store byte, advancing memory pointer SOB %5,10$ ; Decrement count and repeat until all done ; BR OBJECT ; Get another record ROBJECT: CLR SAVLEN ; Clear record length, to force a read CALL BINWRD ; Get store/start address CALL DPADR ; Copy to address MOV SAVLEN,%5 ; See how many bytes left BNE 10$ ; Get data unless address is all there was INCB TRAILER ; Note we had a trailer ASR %1 ; See if address (w/o ADDVAL) is odd BCC 20$ ; Yes, %3/%4 is transfer address CLR %3 ; No, clear %3/%4 to signify none CLR %4 20$: RETURN .DSABL LSB .PAGE .SBTTL DEC PAPER-TAPE ABSOLUTE LOADER FORMAT ; This is the format used by the PDP-11 paper-tape absolute (not bootstrap) ; loader: ; ; 10bbaaddd...dc ; ; 10 indicates start of data ; bb is byte count from 1 to last data byte, lo+hi ; aa is address lo+hi ; dd...d is data bytes ; c is checksum such that 1+0+b+b+a+a+d+...+d+c = 0 ; ; an end block is defined by bb=6, i.e. a block without data, when aa is the ; start address, or none if aa is odd. RABSOLUTE: CLR SAVLEN ; Say no data, so first BINBYT will fetch it ; Look for an 001 byte, signifying start of record. 10$: CALL BINBYT ; Get byte MOV %1,CSUM ; (Re-)set checksum DEC %1 ; Is it 1? BNE 10$ ; No, keep looking CALL BINBYT ; Next byte should be null TSTB %1 BEQ 15$ ; OK if so JMP ITERROR ; Else "Invalid block type" 15$: JSR PC,BINWRD ; Get byte count MOV %1,%5 ; to %5 JSR PC,BINWRD ; Next word = store address CALL DPADR ; Copy address + ADDVAL SUB #6,%5 ; Subtract header count from total BLE 50$ ; -ve is EOF block ; Read and store the %5 bytes in the record, where %3/%4 points. 20$: JSR PC,BINBYT ; Read byte JSR PC,STOCMP ; Store byte, advancing memory pointer SOB %5,20$ ; Decrement count and repeat until all done ; Make sure checksum is right. 30$: CALL BINBYT ; Fetch and test checksum BEQ 10$ ; Get another record if OK 40$: JMP CSERROR ; Else binary checksum error ; End-of-file block. If address (%1 and %3/%4) is odd, no transfer addr, else it ; is given in %3/%4. 50$: INCB TRAILER ; Note we had a trailer ASR %1 ; See if address (w/o ADDVAL) is odd BCC 60$ ; Yes, %3/%4 is transfer address CLR %3 ; No, clear %3/%4 to signify none CLR %4 60$: CALL BINBYT ; Final byte is checksum BNE 40$ ; Error if sum not zero RETURN ; OK if it is .PAGE .SBTTL TASK FILE FORMAT ; ; Programs should be built with TKB (NOT FTB) as follows: ; ; prog.SYS/-HD/-MM/-FP/-SE,map=prog,... ; / ; STACK=ssssss ; UNITS=0 ; ACTFIL=0 ; ASG=0 ; PAR=:lo addr:size ; {TASK=name} ; // ; ; where: ; lo addr is the lowest memory area to be loaded. Must be in octal, and ; a multiple of 100(8). ; size must be a value big enough to contain the given code. A larger ; value than needed may be given; any extra space is not used ; (or zeroed). size must also be a multiple of 100(8). ; ssssss (which nominally sets the stack size) actually sets the breakpoint ; between relocatable PSECTs (above ssssss) and the .ASECT area, ; (below it) otherwise you get overwrite error messages (even if ; you don't have any relocatable code!). Its value should be ; computed as: ; ssssss=(highest absolute address-lo addr)/2 ; words, and entered in decimal. ; ; The resulting task image takes the form of two "label" blocks, followed by the ; task itself, starting in block 3 with data for lo addr onwards. The following ; entries of the first label block only are used:- ; ; L$BTSK (0-3) task name, radix-50 ; L$BSA (10) lo addr ; L$BHGV (12) hi addr ; L$BXFR (350) start address ; L$BHRB (356) offset to task data block from this block .MCALL LBLDF$ LBLDF$ ; Define task header block offsets .PSECT READ,I,RO ; Reset PSECT RTASK: CALL GETREC ; Read first label block IOERROR ; Not even EOF allowed MOV %0,%5 ; Copy buffer pointer MOV #RNM+RNMLEN-8.,%0 ; Buffer for program name MOV (%5)+,%1 ; which is in first 2 words of 1st block JSR PC,$C5TA ; in Radix-50 MOV (%5)+,%1 ; 6 ASCII bytes JSR PC,$C5TA ; (leave last 2 spaces, as preset) PUSH L$BXFR-4(%5) ; Save transfer address MOV L$BSA-4(%5),%1 ; Get lo load address CALL DPADR ; to %3/%4 PUSH L$BHGV-4(%5) ; Get hi-lo+1 SUB %1,@SP ; Giving no of bytes in file INC (SP) ; + 1 for inclusive subtraction ;01APR5 MOV L$BHRB-4(%5),%5 ; Get no of remaining header blocks before data 40$: CALL GETREC IOERROR SOB %5,40$ ; and ignore them ; Read data straight from file blocks. 45$: MOV %1,%5 ; Load block size (512.) 50$: MOVB (%0)+,%1 ; Fetch a byte CALL STOCMP ; Store or compare DEC @SP ; Reached last address? ;01APR5 BEQ 100$ ; Done if so ;01APR5 SOB %5,50$ ; Repeat while some to do INC %5 ; Read 1 BR 40$ ; new block 100$: POP ; Purge count from stack MOV @SP,%1 ; Fetch transfer address ROR (SP)+ ; Is it odd? BCS 110$ CALLR DPADR ; No, include ADDVAL and return 110$: JMP NOXFER ; Odd transfer address means none given .PAGE ;01APR5 .SBTTL COMPARE MEMORY ;01APR5 ;01APR5 ; This is a very simple module, used with COMPARE only, when FILE xxx is ;01APR5 ; replaced by WITH address. ;01APR5 ; ;01APR5 ; We simply compare the region given by the FROM and THRU addresses with ;01APR5 ; memory starting at the WITH address, as if the latter were a file. ;01APR5 ; ;01APR5 ; On entry, R0 contains the offsetted and validated WITH address, from ;01APR5 ; GETHXL/VALID, and FROM,THRU, and STEP are set from the command line, (or ;01APR5 ; defaulted from a previous one). ;01APR5 ;01APR5 RMEMORY:MOV FROM,R2 ; Fetch FROM address ;01APR5 CALL UNOFFSET ; Add offset ;01APR5 CALL ADDADV ; and ADDVAL, giving R3/R4 ;01APR5 CALL VALID ; Validate ;01APR5 BCS 30$ ; Exit if error ;01APR5 MOV COUNT,R5 ; Fetch count ;01APR5 10$: MOVB MEMORY(R0),R1 ; Fetch a byte ;01APR5 CALL STOCMP ; Compare ;01APR5 INC R0 ; Advance memory pointer ;01APR5 BMI 20$ ; Out of memory if bit 15 gets set ;01APR5 SOB R5,10$ ; Repeat until done ;01APR5 JMP NOXFER ; No transfer address, just report ;01APR5 ;01APR5 ; Gone out of HEX's memory. ;01APR5 20$: OUTPUT AOR ; "Address out of range" ;01APR5 30$: JMP ERREXIT ; Abort (no file to close) ;01APR5 .PAGE .SBTTL READ DATA AREAS .PSECT DATA RW,D ADDVAL: .BLKL ; USER SUPPLIED OFFSET TO BE ADDED TO ADDRESS ; UPON READING SAVLEN: .BLKW ; SAVED LENGTH OF INPUT RECORD IN CASE OF ERROR DCOUNT: .BLKW ; Number of differing bytes found on compare CSUM: .BLKW ; TEMPORARY SCRATCHPAD WHILE COMPUTING CHECKSUM RETSP: .BLKW ; SP->return address for error exits from anywhere CMPFLG: .BLKB ; COMPARE/READ FLAG 0->COMPARE, 1-> READ PART: .BLKB ; 0 --> FULL FILE, 1 --> NO TRAILER RECORD TRAILER:.BLKB ; 0 --> TRAILER RECORD NOT SEEN IN THIS FILE ; NZ -> TRAILER RECORD ENCOUNTERED ERRFLG: .BLKB ; Error flag .IIF DF P$$OFF,WRNFLG: .BLKB ; Warning flag ; Mismatch messages. AMF is whole, for file/memory compare, AM1 and AM2 are ;01APR5 ; concatenated subsets for memory-memory (COMPARE ... WITH). ;01APR5 DEFM AM1 ;01APR5 .ASCII "File: 00= C" ;01APR5 AMF=AM1 ; AMF starts with CR ;01APR5 AMFLEN=.-AMF ; and goes up to "File..." ;01APR5 AM2=AM1+1 ; No initial CR ;01APR5 AM2LEN=AM1LEN-2 ; or trailing tab ;01APR5 DEFM DCT <0000 differences> .EVEN ; Remaining scratch area is used in different ways by different read processors, ; all overlaid. First two words are cleared on entry. ;01APR5 COMMON: ; Intel CURSBA: .BLKL 1 ; Current segment base address ;01APR5 ; Extended TekHex: .=COMMON ;01APR5 BLKLEN: .BLKB 1 ; LENGTH OF BLOCK, NOT INCLUDING LEADING ; % OR END OF LINE TERMINATOR .=COMMON ; Rockwell: RECCNT: .BLKL ; No of records read .=COMMON ; Hex/octal: DGTSUB: .BLKW ; Digit check routine, OCTDGT or HEXDGT ADREND: .BLKB 1 ; Address flag for HEX/OCTAL format .=COMMON ; RCA .BLKW ; Uses DGTSUB, as HEX/OCTAL RCATYP: .BLKW 1 ; <>0 = load address defined .=COMMON ; Mostek: .BLKB 1 ; NUMBER OF DATA BYTES (BLKLEN) MOSTYP: .BLKB 1 ; TYPE, 0 --> NO HEADER READ YET, ; 16 --> 16-BIT ADDRESS MODE, ; 32 --> 32-BIT ADDRESS MODE. .=COMMON ; Whitesmiths': CONFIG: .BLKB 1 ; Configuration byte .PSECT PURE RO,D ; Format processor lookup table. N.B.B. The order MUST match the definitions ; in HEX, and equivalent tables in FORMAT and WRITE. Bit 0 set means this is ; a binary format, so record shouldn't be displayed on error. FORMTS: .WORD RINTEL ; 0 .WORD RMOTOROLA ; 2 .WORD RROCKWELL ; 4 .WORD RRCA ; 6 .WORD RTEKHEX ; 10 .WORD REXTENDED ; 12 .WORD RTEXAS ; 14 .WORD RMOSTEK ; 16 .WORD RWHITESMITHS!1 ; 20 .WORD RRIM!1 ; 22 .WORD RBIN!1 ; 24 .WORD RHEX ; 26 .WORD ROCTAL ; 30 .WORD RTCI ; 32 .WORD RFAIRCHILD ; 34 .WORD RSIRA!1 ; 36 .WORD ROBJECT!1 ; 40 .WORD RABSOLUTE!1 ; 42 .WORD RTASK!1 ; 44 .WORD RMEMORY!1 ; Extra entry for COMPARE WITH ;01APR5 DEFM HRR
DEFM IAS DEFM IBC DEFM IBT DEFM TRL DEFM CSE DEFM TRI .EVEN .END