ALWAYS 23MAR4 WRITE ;23MAR4 .MCALL DIR$,PUT$S,CLOSE$,OPEN$,FDAT$R,CALLR ;**-1 .MACRO NOTIMP FORMAT H.'FORMAT=ILLFMT S.'FORMAT=ILLFMT B.'FORMAT=ILLFMT E.'FORMAT=ILLFMT T.'FORMAT=ILLFMT .ENDM ;************************************************************************ ;* * ;* MODULE: WRITE * ;* * ;* FUNCTION: WRITE A HEX OUTPUT FILE * ;* * ;* INCLUDES: APPEND to existing file * ;* * ;* INPUT PARAMETERS: * ;* * ;* R0 POINTS TO COMMAND LINE IN PROCESS * ;* * ;* OUTPUT PARAMETERS: * ;* * ;* DESTROYS ALL REGISTERS * ;* * ;* AUTHOR: KEVIN ANGLEY * ;* * ;* DATE: 30-AUG-82 * ;* * ;* MODIFIED BY: Chris Doran, Sira Ltd. * ;* * ;* DATE: Jan 84 * ;* * ;* Make WIDTH default format-dependent. * ;* Change PUT$'s to PUT$S's for non-FCSRES overlaid * ;* version as READ and WRITE are in different overlays. * ;* Major re-write for additional formats. * ;* Add APPEND option. * ;* Suppress statistics report if NOECHO mode selected. * ;* * ;* 23MAR4 Scott Smith, Telex Computer Products, Raleigh, NC * ;23MAR4 ;* Included a conditional assembly block that changes all * ;23MAR4 ;* 16 byte default output widths to 32 bytes * ;23MAR4 ;* * ;23MAR4 ;************************************************************************ APPEND:: CMP RWFORMAT,#F.TASK ; Append mode is illegal for TASK BEQ 10$ CMP RWFORMAT,#F.WHITESMITHS ; and Whitesmiths' formats BEQ 10$ MOVB #FO.APD,FDB+F.FACC ; Set to open for append BR WACOM ; Join common code 10$: OUTPUT IAP ; Can't append in current format BR ERRORX ; Return with cs = error WRITE:: MOVB #FO.WRT,FDB+F.FACC ; Require new file WACOM: MOV SP,RETSP ; Save pointer to return addr for error exits MOVB #377,ERRFLG ; Set exit status to error CLRL ADDVAL ; ASSUME NO OFFSETTING UPON READING ADDRESSES CLRB PART ; ASSUME NO PARTIAL WRITE CLR BCOUNT ; Clear total counter CLR TOTCSM ; Clear total checksum MOV RWFORMAT,%4 ; Get file format BMI ILLFMT ; QA check that it's legal. -ve isn't CMP %4,#F.MAX BHI ILLFMT ; > F.MAX isn't BIT #1,%4 ; and odd isn't BEQ GETSUB ; OK, continue ILLFMT: OUTPUT UFS ; Else "unsupported format" ERRORX: SEC ; Say error CALLR EXTRA ; Give up ; Get addresses of common output subroutines: GETSUB: .IF DF M$$EIS MUL #7,%4 ; Table is 7 entries wide MOV %5,%4 ; Keep (lo) index in %4 .IFF PUSH %4 ; Table is 7 entries wide ASL %4 ; Multiply %5 by 7 ASL %4 ; which is same as ASL %4 ; multiplying by 8 SUB (SP)+,%4 ; and subtracting once .ENDC ADD #TABLE,%4 ; Point into table MOV (%4)+,HSUB ; Start of file output MOV (%4)+,SSUB ; Start of record output MOV (%4)+,BSUB ; Byte-by-byte output MOV (%4)+,ESUB ; End-of-record output MOV (%4)+,TSUB ; End of file output .PAGE .SBTTL COLLECT KEYWORDS CALL FROMTH ; COLLECT FROM/THRU BCS ERRORX ; TAKE ERROR EXIT GETKEY PLUS ; TRY FOR PLUS KEYWORD BNE 141$ ; NE: NO GOTS CALL GETHXL ; GET THE PLUS ADDRESS BCS ERRORX ; TAKE ERROR EXIT 141$: GETKEY MINUS ; TRY FOR MINUS KEYWORD BNE 146$ ; NE: NO GOTS CALL GETHXL ; GET THE MINUS ADDRESS BCS ERRORX ; TAKE ERROR EXIT NEG R2 ; NEGATE THE MINUS ADDRESS NEG R1 SBC R2 144$: MOV R1,ADDVAL ; SET UP ADDVAL MOV R2,ADDVAL+2 146$: MOV (%4)+,%5 ; Get default bytes/record BMI 149$ ; not allowed for TASK or Whitesmith's format GETKEY WIDTH ; Get WIDTH keyword BNE 149$ ; Default if not given TSTB -1(%4) ; For big records (HEX and OCTAL types) BEQ 147$ ; default WIDTH > 256. CALL GETHX4 ; Need a 4-byte number BR 148$ 147$: CLR %1 ; Others need only 2 digits, clear hi byte CALL GETHX2 ; Get lo 148$: BCS ERRORX ; Trap conversion error MOV %1,%5 ; OK, copy result 149$: BIC #100000,%5 ; Strip no-WIDTH flag BEQ 150$ ; ZERO IS ILLEGAL CMP @%4,R5 ; CANNOT EXCEED MAXIMUM WIDTH BHIS 151$ ; LOS: DOES NOT 150$: OUTPUT BDW ; BAD WIDTH BR ERRORX ; TAKE ERROR EXIT 151$: MOV %5,WIDTH ; SAVE THE WIDTH GETKEY PARTIAL ; TRY FOR PARTIAL READ BNE 153$ ; NE: NO GOTS INCB PART ; SET PARTIAL FLAG 153$: GETKEY FILE ; GET FILE KEYWORD BEQ 154$ ; EQ: GOT IT OUTPUT MSK ; MISSING KEYWORD BR ERRORX ; TAKE ERROR EXIT 154$: CALL PARSE ; PARSE THE FILE DESCRIPTOR BCS ERRORX ; CS: PARSE FAILURE - TAKE ERROR EXIT FDAT$R #FDB,,,WIDTH ; Set F.RSIZ for fixed record files ; (Whitesmith's and task) OPEN$ ; Open the file for WRITE or APPEND BCC START ; CC: OPENED O.K. OPENERR: MOV #FOE+FOELEN-4,%0 ; Address space for error code MOV FDB+F.ERR,%1 ; Fetch it CALL PUTHX4 ; Insert hex code MOV #FOE,OUTDIR+Q.IOPL MOV #FOELEN,OUTDIR+Q.IOPL+2 JMP ERRMSG ; Print message and close it (if FCS didn't) .PAGE .SBTTL FILE GENERATION ; File generation consists of five operations: ; 1. File header output -- H.xxx entries ; 2. Start record of WIDTH bytes -- S.xxx entries ; 3. Output WIDTH bytes -- B.xxx entries ; 4. End record -- E.xxx entries ; Repeat from 2 until all done, then: ; 5. Trailer output -- T.xxx entries ; All of these are format-dependent, selected by RWFORMAT. ; ; REGISTER USAGES: ; ; Headers, H.xxx entries:- ; %0 -> start of output buffer ; %1/%2 = real transfer address, excluding ADDVAL ; %3 -> PRGNAM ; %4 = length of PRGNAM, excluding trailing blanks ; %5 = length of first record ; carry is set if appending to old file (when header may be suppressed) ; clear when creating new one. ; %5 should be updated (if necessary) for required first record length. No ; other registers need be preserved. ; ; Record start, S.xxx entries:- ; %0 -> start of output buffer ; %1/%2 = real address, including ADDVAL ; %3/%4 = ditto ; 2(SP) = offsetted address ; %5 = bytes left in (max length) record ; CSUM = 0, ready for checksum ; %1 & %2 may be destroyed, %0 updated, %3-%5 must be preserved. ; ; Byte output, B.xxx entries:- ; %0 -> current location in record ; %1 = value to be written (low byte, hi byte clear) ; %2 = offsetted address (pointer into MEMORY) ; %3/%4 = real current address, including ADDVAL ; %5 = contains decrementing data count for current record ; CSUM = checksum ; RECBYT = number of bytes output in current record, including this ; BCOUNT = grand total of bytes output, including this ; %1 may be destroyed, %0 updated, %2-%5 must be preserved. ; ; End of record, E.xxx entries:- ; %0 -> current location in output buffer ; %1 = no of data bytes output in this record ; %2 = offsetted address of next data byte ; %3/%4 = real address of next byte ; %0, %1 and %5 may be destroyed, %2-%4 must be preserved. ; ; End of file, T.xxx entries:- ; %0 -> start of output buffer ; %1/%2 = transfer address, excluding ADDVAL ; No registers need be preserved. ; ; Set byte count of first record so that subsequent ones will start at addresses ; which are exact multiples of WIDTH. First record length is therefore ; WIDTH - ((FROM DIV STEP) MOD WIDTH) ; Any format (e.g. TASK) which doesn't like this should reset %5. ; Note: this is more than cosmetic -- HEX and OCTAL formats require it for ; PROM padding. START: CALL ADD1ST ; Get first address, including ADDVAL, to %1/%2 MOV STEP,%0 ; Get STEP CALL $DDIV ; Divide (unsigned) quotient still in %1/%2 MOV %5,%0 ; Get WIDTH to %0 CALL $DDIV ; Divide unsigned again SUB %0,%5 ; Subtract remainder for short first record ; 1. FILE HEADER MOV FDB+F.NRBD+2,%0 ; Address buffer start MOV TRNSFR,%1 ; May need start address MOV TRNSFR+2,%2 MOV #PRGNAM,%3 ; or program name MOV #PRGNAM+8.,%4 ; Max 8 chars 13$: CMPB -(%4),#SPACE ; Trim trailing spaces BNE 14$ ; Until non-space character found, CMP %4,%3 ; Or we reach start of name (with space prefix) BHIS 13$ ; (Back 1 too far compensates for up-coming INC) 14$: SUB %3,%4 ; Compute length INC %4 ; Including char we point to CLR CSUM ; Clear (header) checksum CLR COMMON ; Clear common workspace (RIMWRD/RECCNT etc) CLR RECBYT ; Clear record bytes CMPB FDB+F.FACC,#FO.APD ; Set carry if open for append CLC ; else clear it BNE 15$ ; For WRITE SEC 15$: CALL @HSUB ; Do header record MOV LOBOUND,R3 ; Get FROM addr MOV LOBOUND+2,%4 ; including offset ADD ADDVAL,%3 ; and ADDVAL ADC %4 ADD ADDVAL+2,%3 MOV FROM,%2 ; Get offsetted value ; 2. START RECORD 20$: MOV FDB+F.NRBD+2,%0 ; Address buffer CLR CSUM ; Clear record checksum CLR RECBYT ; Clear record bytes PUSH %2 ; Save offsetted address MOV %3,%1 ; Copy real address to %1 MOV %4,%2 ; and %2 for PUTHXx CALL @SSUB ; Output record start code POP %2 ; Restore offsetted address ; 3. OUTPUT WIDTH BYTES 30$: CLR %1 ; Get a byte BISB MEMORY(%2),%1 ; Lo only ADD %1,TOTCSM ; Add to WRITE's checksum INC BCOUNT ; Count total bytes output INC RECBYT ; and in this record CALL @BSUB ; Write it out INCR34 STEP ; Advance real address, ADD STEP,%2 ; and memory pointer by STEP CMP %2,THRU ; Reached very last? BHI 50$ ; Yes, end file SOB %5,30$ ; No, repeat through this record ; 4. END RECORD MOV RECBYT,%1 ; End of record, load no of bytes output CALL @ESUB ; Complete record, and output CLR RECBYT ; Clear bytes in record counter MOV WIDTH,%5 ; Re-load max record length BR 20$ ; Go start another ; 5. END FILE 50$: MOV RECBYT,%1 ; See if partial record written BEQ 55$ ; No, just end here CALL @ESUB ; and complete final, short, record 55$: TSTB PART ; Trailer to be suppressed? BNE 57$ ; Yes, just report MOV TRNSFR,%1 ; May need start address for trailer MOV TRNSFR+2,%2 MOV FDB+F.NRBD+2,%0 ; Address buffer CLR CSUM ; Clear checksum CALL @TSUB ; Output file trailer .PAGE .SBTTL REPORT 57$: CLRB ERRFLG ; Success if we come back here! TST QUIET ; No-echo mode on command file selected? BEQ CLOSE ; Yes, suppress statistics display. Just exit 571$: MOV #RSMLEN-RDTLEN-2,OUTDIR+Q.IOPL+2 ; Length w/o TRNSFR MOV TRNSFR+2,R2 ; Get transfer address MOV TRNSFR,R1 BNE 58$ ; Go output if there is one: lo <> 0 TST %2 ; or hi <> 0 BEQ 60$ ; Don't print transfer if there isn't one 58$: MOV #RDT+RDTLEN-8.,R0 CALL PUTHXJ ADD #RDTLEN+2,OUTDIR+Q.IOPL+2 ; Add to length 60$: MOV LOBOUND,R1 MOV LOBOUND+2,R2 MOV #RDL+RDLLEN-9.,R0 CALL PUTHXJ TST BCOUNT ; WAS THERE ANY DATA REALLY WRITTEN? BNE 228$ ; NE: ABSOLUTELY CLRL LOBOUND ; EQ: NOT ANY, MUST CLEAR STATISTICS ; (LONG WORD) 228$: MOV HIBOUND,R1 ; PUT HIGHEST ADDR ENCOUNTERED IN MESSAGE MOV HIBOUND+2,R2 MOV #RDH+RDHLEN-9.,R0 CALL PUTHXJ MOV BCOUNT,R1 ; PUT BYTE COUNT INTO MESSAGE MOV #RDC+RDCLEN-5,R0 CALL PUTHX4 MOV TOTCSM,R1 ; PUT CHECKSUM IN MESSAGE MOV #RDS+RDSLEN-5,R0 CALL PUTHX4 MOV #RDL,OUTDIR+Q.IOPL ; SET UP OUTPUT STATISTICS ERRMSG: DIR$ #OUTDIR CLOSE: CLOSE$ #FDB ERREXIT: MOV RETSP,SP ; Purge stack on error RORB ERRFLG ; Copy error flag to carry NOOP: ; RETURN for formatting routines which do nothing RETURN .PAGE .SBTTL BYTE/WORD WRITE ROUTINES ; Write byte or word in %1 where %0 points, updating checksum in %5 according ; to file format. .ENABL LSB ; Intel and similar writes -- add byte(s) to checksum and output hex value. ; Do a proper 16-bit sum, for Rockwell use. ; Output 32-bit address if MODE<>16. PUT32: CMP MODE,#16. ; If 16-bit mode BEQ PUTWRD ; Just output 16 bits and return PUSH %1 ; Else save lo word MOV %2,%1 ; Copy hi CALL PUTWRD ; Output that BR 5$ ; Go pop lo and write that too ; Output 24-bit address if MODE<>16. PUT24: CMP MODE,#16. ; If 16-bit mode BEQ PUTWRD ; Just output 16 bits and return PUSH %1 ; Save lo word MOVB %2,%1 ; Get hi 8 bits CALL PUTBYT ; Output bits 16-23 5$: POP %1 ; Restore lo 16 ; BR PUTWRD ; Store and return ; Output hex word, hi byte first. PUTWRD: CALL 10$ ; Output hi byte 10$: SWAB %1 ; Swap bytes (back) PUTBYT: PUSH %1 ; Save whole word BIC #^C377,%1 ; Clear hi byte ADD %1,CSUM ; Add byte POP %1 ; Restore whole word PUTHE2: CALLR PUTHX2 ; Output and return .DSABL LSB ; Output binary word from %1, adding bytes to (8-bit) checksum. BINWRD: CALL BINBYT ; Lo byte first SWAB %1 ; Swap bytes for hi ; CALLR BINBYT ; Output hi and return ; Output binary byte, adding to (8-bit) checksum. BINBYT: ; Byte write MOVB %1,(%0)+ ; Copy into buffer ADD %1,CSUM ; Add to checksum RETURN ; and return .PAGE .SBTTL MISCELLANEOUS SUBROUTINES ; Set %1/%2 to LOBOUND+ADDVAL = first real store address. ADD1ST: MOV LOBOUND,%2 ; Fetch first address MOV LOBOUND+2,%1 ; including offset ADD ADDVAL,%2 ; and ADDVAL ADC %1 ADD ADDVAL+2,%1 RETURN ; Make sure record widths, including that of first record (in %5) are even. ; Called by H.XXX for formats that output words not bytes. ; Makes sure WIDTH and %5 are even by adding 1 if necessary. ; Preserves state of carry and all other registers. EVENWIDTH: INC WIDTH ; Make sure WIDTH is even BIC #1,WIDTH ; For storing words INC %5 ; Same goes for first record counter BIC #1,%5 RETURN .PAGE .SBTTL WRITE RECORD ; Enter with %0-> end of record. Compute length, write to file, with error ; check, and return if OK. ENDCSM: ; Enter here to append c/sum byte in %5 MOV CSUM,%1 ; Copy checksum CALL PUTHX2 ; Append to record ; BR PUTREC ; Output record and return PUTREC: SUB FDB+F.NRBD+2,%0 ; Compute no of bytes in record PUTCNT: MOV %0,FDB+F.NRBD ; Put it into FDB PUTBLK: PUT$S #FDB ; Output record MOV FDB+F.NRBD+2,%0 ; Re-address buffer for next time BCC NOOP ; RETURN to caller if OK MOV #IOE+IOELEN-4,%0 ; Address space for error code MOV FDB+F.ERR,%1 ; Fetch it CALL PUTHX4 ; Insert hex code MOV #IOE,OUTDIR+Q.IOPL MOV #IOELEN,OUTDIR+Q.IOPL+2 BR ERRMSG ; Close file and exit .PAGE .SBTTL INTEL FORMAT OUTPUT ; :bbaaaattdddd...ddcc ; where: ; bb = byte count ; aaaa = address ; tt = block type: 00 = data, 01 = end, no SA, 02 = end, SA ; dd...dd = data bytes ; cc = checksum, -(bb+aa+aa+tt+dd+...+dd) .ENABL LSB H.INTEL=NOOP ; No special file start S.INTEL: ; Record start CLR %2 ; Default record type is 0 10$: MOVB #':,(%0)+ ; Begin with colon CMPB (%0)+,(%0)+ ; Leave byte count for E.INTEL CALL PUTWRD ; Put address into record (lo word only) MOV %2,%1 ; Get record type BR PUTBYT ; Load type and return, addressing data B.INTEL=PUTBYT ; Byte write done directly T.INTEL: ; File trailer MOV #1,%2 ; Record type is 1 TST %1 ; If no SA BEQ 20$ INC %2 ; 2 if SA given 20$: CALL 10$ ; Start record with address CLR %1 ; Byte count is 0 ; BR E.INTEL ; Do checksum like E.INTEL E.INTEL: ; Record end PUSH %0 ; Save end of record pointer MOV #RECORD+1,%0 ; Whilst addressing byte count point CALL PUTBYT ; Write byte count POP %0 ; Restore end of record pointer NEG CSUM ; Checksum is - byte sum BR ENDCSM ; Append to end of record, output, and return .DSABL LSB .PAGE .SBTTL WRITE MOTOROLA FORMAT FILE ; Stbbaaaadddd...ddcc ; where: ; t = block type: 0 = header, 1 = data, 9 = EOF ; bb = byte count, aa...cc inclusive ; aaaa = load address ; dd...dd = data bytes ; cc = checksum such that bb+aa+aa+tt+dd+...+dd+cc = $FF ; Header record: S0bbaaaannnn...nncc ; nn...nn is program NAM, hex-encoded ASCII H.MOTOROLA: ; File start BCS 10$ ; No header if appending TST %4 ; or no name BEQ 10$ MOV #"S0,(%0)+ ; Header starts S0 TST (%0)+ ; Byte count done later CLR %1 ; "Address" is 0000 CALL PUTHX4 ; PUTHX4 is easiest way to do it PUSH %4 ; Save length of name 5$: MOVB (%3)+,%1 ; Fetch a byte of name CALL PUTBYT ; Output as a hex byte SOB %4,5$ ; Until all done POP %1 ; Load byte count = length of name CALLR EMR ; Complete and output record and return 10$: RETURN ; For normal data start S.MOTOROLA: ; Record start MOV #"S1,(%0)+ ; Record type is S1 TST (%0)+ ; Skip count for the present CMP MODE,#16. ; If 16-bit mode BEQ PUTWRD ; Just output 16 bits and return INCB RECORD+1 ; S2 if 24-bit BR PUT24 ; Output 24-bit address B.MOTOROLA=PUTBYT ; Byte write done directly ; Trailer record is S903aaaacc or S804aaaaaacc. T.MOTOROLA: ; File trailer MOV #"S9,(%0)+ ; Assume record type is 9 TST (%0)+ ; Skip count for the moment CMP MODE,#16. ; If not 16-bit mode BEQ 10$ DECB RECORD+1 ; Record type is S8 10$: CALL PUT24 ; Output 16 or 24-bits as necessary CLR %1 ; No data bytes ; BR E.MOTOROLA ; End like any other record E.MOTOROLA: ; Record end CMP MODE,#16. ; If 24-bit mode BEQ EMR INC %1 ; Address is 1 byte more EMR: ADD #3,%1 ; Byte count = data + address + checksum PUSH %0 ; Save end of record pointer MOV #RECORD+2,%0 ; Whilst addressing byte count point CALL PUTBYT ; Write byte count there POP %0 ; Restore end of record pointer COM CSUM ; Checksum is ~ byte sum BR ENDCSM ; Append to end of record, output, and return .PAGE .SBTTL WRITE ROCKWELL FORMAT FILE ; ;bbaaaadddd...ddcccc ; where: ; bb = no of data bytes, dd...dd ; aaaa = start address ; cccc = checksum bb+aa+aa+dd+...+dd ; H.ROCKWELL=NOOP ; No special file start (RECCNT cleared above) S.ROCKWELL: ; Record start MOVB #';,(%0)+ ; with semicolon CMPB (%0)+,(%0)+ ; Skip byte count CALLR PUTWRD ; Store address (lo) B.ROCKWELL=PUTBYT ; Byte write done directly T.ROCKWELL: ; File trailer is MOV RECCNT,%1 ; aaaa = no of records output CALL S.ROCKWELL ; Start record CLR %1 ; 0 data bytes ; BR E.ROCKWELL ; Finish and return, as usual E.ROCKWELL: ; Record end PUSH %0 ; Save end of record pointer MOV #RECORD+1,%0 ; Whilst addressing byte count point CALL PUTBYT ; Write byte count there POP %0 ; Restore end of record pointer INC RECCNT ; Count records MOV CSUM,%1 ; Checksum is whole word sum CALL PUTHX4 CALLR PUTREC ; Output and return .PAGE .SBTTL WRITE RCA FORMAT FILE ; aaaa dd dd dd ...dd ; ; where: ; aaaa = load address ; dd ... = data ; individual items end with spaces, end of record is a semicolon. .ENABL LSB H.RCA=NOOP ; No special file start S.RCA: ; Record start CALL PUTHX4 ; Write address BR 10$ ; Then space and return B.RCA: ; Write byte CALL PUTHX2 ; Just the byte itself 10$: MOVB #SPACE,(%0)+ ; And a trailing space RETURN E.RCA: ; Record end MOVB #';,(%0)+ ; Just append a semicolon CALLR PUTREC ; Output and return T.RCA=NOOP ; No file trailer .DSABL LSB .PAGE .SBTTL WRITE 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 H.TEKHEX=NOOP ; No special file start S.TEKHEX: ; Record start MOVB #'/,(%0)+ ; Start line with slash CALL 10$ ; Write word MOV CSUM,HDCSUM ; Save header checksum so far ADD #4,%0 ; Skip byte count and checksum, for data start CLR CSUM ; Clear data checksum RETURN 10$: ; Write word, adding nybbles to checksum CALL @PC ; Call following code twice: SWAB %1 ; Swap bytes B.TEKHEX: ; Write byte, adding nybbles to checksum PUSH %1 ; Save value ASH #-4,%1 ; Shift down hi nybble CALL 10$ ; Write, adding to checksum MOV @SP,%1 ; Fetch back CALL 10$ ; For lo nybble POP %1 ; Restore original value RETURN ; and return 10$: BIC #^C^B1111,%1 ; Select lo nybble only ADD %1,CSUM ; Add to checksum CALLR PUTHX1 ; Output hex digit and return .ENABL LSB E.TEKHEX: ; Record end PUSH %1 ; Save data byte count MOV CSUM,%1 ; Get data checksum CALL PUTHX2 ; Append to record POP %1 ; Restore byte count 10$: PUSH %0 ; Save end of record pointer MOV #RECORD+5,%0 ; Whilst addressing byte count point MOV HDCSUM,CSUM ; Get header checksum so far (address only) CALL B.TEKHEX ; Write byte count MOV CSUM,%1 ; Get header checksum CALL PUTHX2 ; Store that too POP %0 ; Restore end of record pointer CALLR PUTREC ; Output record and return ; Trailer is start address (if any), and byte count 0. ; Output a // abort block if there isn't a trailer. T.TEKHEX: ; File trailer BIS %1,%2 ; See if there is a transfer address BEQ ABOTKH ; Do abort block if not 20$: CALL S.TEKHEX ; Start record in the normal way CLR %1 ; Byte count 0 BR 10$ ; Complete record (header only) and return ; No start address, end with an abort block instead. ABOTKH: MOV #ABOBLK,FDB+F.NRBD+2 ; Set message address MOV #ABOLEN,%0 ; and length CALLR PUTCNT ; Output and return .DSABL LSB .PAGE .SBTTL WRITE EXTENDED TEKHEX FORMAT FILE ; %bbtccna...addd...dd ; ; where: ; bb is character count, bb (inclusive) to end ; t is type: 3 = symbol definition (header), 6 = data, 8 = trailer. ; cc is checksum, sum of all bytes except itself and %, with special ; character coding, see CKSUM. ; na...a is a variable-length address ; d...d is data bytes .ENABL LSB ; File header is a type 3 block, with special format: ; %bb3ccn0nn H.EXTENDED: BCC 5$ ; No header if appending 1$: RETURN 5$: TST %4 ; or no name BEQ 1$ ADD #6,%0 ; Else start at byte 6 MOV %4,%1 ; with length of name CALL PUTHX1 ; 1 hex digit 10$: MOVB (%3)+,(%0)+ ; Copy name itself SOB %4,10$ MOVB #'0,(%0)+ ; 0 to introduce section definition field CALL ADD1ST ; Which is lo address of module, inc ADDVAL CALL PUTHXV ; Variable-length hex number MOV COUNT,%1 ; Get count CLR %2 ; Single-precision CALL PUTHXV ; Variable-length again MOVB #'3,ETKTYP ; Type 3 block BR 100$ ; Go complete and output S.EXTENDED: ; Start data record ADD #6,%0 ; Leave header for later CALLR PUTHXV ; Insert variable-length load address and return B.EXTENDED=PUTHE2 ; Output byte direct E.EXTENDED: ; End data record MOVB #'6,ETKTYP ; Load record type flag ; End of (any) record -- fill in header, computing checksum. 100$: PUSH %0 ; Save end pointer PUSH %3 ; and %3 MOV %0,%3 ; Copy pointer MOV #RECORD,%0 ; Point back to start of record MOVB #'%,(%0)+ ; All records begin % SUB %0,%3 ; Set %3 = record length, excluding % MOV %3,%1 ; Insert record length CALL PUTHX2 MOVB ETKTYP,(%0)+ ; and block type MOVB #'0,@%0 ; Then two zeroes MOVB (%0)+,(%0)+ ; For checksum not included in itself SUB #5,%0 ; Point back to start of record 110$: MOVB (%0)+,%1 ; Get a character CALL CKSUM ; Convert code and add to checksum SOB %3,110$ ; Repeat for all string MOV CSUM,%1 ; Load checksum MOV #RECORD+4,%0 ; Point to space for it CALL PUTHX2 ; Store it there POP %3 ; Restore %3 POP %0 ; and end-of-record pointer CALLR PUTREC ; Output and return T.EXTENDED: ; Trailer block PUSH %1 ; See if we have a start address BIS %2,(SP)+ BNE 120$ ; Yes, store it JMP ABOTKH ; No, end with abort block instead 120$: CALL S.EXTENDED ; Put in start address MOVB #'8,ETKTYP ; Block type is 8 BR 100$ ; End file .DSABL LSB ; Find character value corresponding to ASCII code of char in %1, and add it to ; CSUM. This is more-or-less a copy of the routine of the same name in READ. 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 CLOSE ; GET OUT OK: ADD R1,CSUM ; ADD TO CHECK SUM RETURN .PAGE .SBTTL WRITE TEXAS FORMAT FILE ; tddddtddddtdddd... ; where: ; t = record type ("tag character"): ; 0 = program name (00000nnnnnnnn) ; 1 = start address ; 7 = checksum, -(sum of all 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. ; ; Addresses should be even, since whole words are stored, but don't check this, ; in case hi or lo bytes only are being written. .ENABL LSB H.TEXAS: ; File start CALL EVENWIDTH ; Make sure widths are even BCS 4$ ; No header record if appending TST %4 ; or no program name BNE 5$ ; Yes, go store it 4$: RETURN ; No header record if no name 5$: MOVB #'0,(%0)+ ; Else header type 0 CLR %1 ; + relocatable code length 0000 (none) CALL PUTHX4 MOV #8.,%4 ; Load byte count for name, always full 8 chars 10$: MOVB (%3)+,(%0)+ ; Copy character of name SOB %4,10$ ; Repeat until all done ; BR E.TEXAS ; Complete and output record E.TEXAS: ; Record end CLR %1 ; Clear %1 for checksum or dummy null BIT #1,RECBYT ; Should have ended on a full word BEQ 60$ ; OK if so CALL PUTHX2 ; Else insert a dummy null INC COUNT ; Count it 60$: MOVB #'7,(%0)+ ; Checksum tag is '7' ; Record checksum is -(sum of ASCII characters up to here). PUSH %3 ; Save %3 MOV %0,%3 ; Copy end of record pointer MOV #RECORD,%0 ; Load start CLR %1 ; Clear checksum SUB %0,%3 ; Compute length CLR -(SP) ; Make space on stack 70$: MOVB (%0)+,@SP ; Fetch character SUB @SP,%1 ; Subtract from checksum SOB %3,70$ ; Repeat through record POP ; Purge stack POP %3 ; Restore %3 CALL PUTHX4 ; Append checksum to record MOVB #'F,(%0)+ ; Finally, an F CALLR PUTREC ; Output record and return S.TEXAS: ; Record start MOVB #'9,(%0)+ ; Always have an address CALLR PUTHX4 ; Put it into record and return ; Only whole words are stored, even bytes first, preceded by B. B.TEXAS: ; Byte write BIT #1,RECBYT ; Is this an odd byte? BEQ 50$ ; Yes if bit clear, just put in byte MOVB #'B,(%0)+ ; No, even, start with a B 50$: CALLR PUTHX2 ; Then data byte T.TEXAS: ; File trailer BIS %1,%2 ; See if we have a transfer address BEQ 80$ ; No, just do EOF record MOVB #'1,(%0)+ ; Yes, tag character is 1 CALL PUTHX4 ; Output transfer address (lo word only) CALL 60$ ; Do checksum 80$: ; EOF record -- : and HFE version no MOV #IDT+1,FDB+F.NRBD+2 ; Address ident & version number MOV #IDTLEN-1,%0 ; Load length, excluding CR prefix CALLR PUTCNT ; Output and return .DSABL LSB .PAGE .SBTTL WRITE MOSTEK FORMAT FILE ; General record has the form: ; ttbbbbdd...ddcc ; where: ; tt is type: F0 = header, F2 = "enumerated" (contiguous) data, ; F4 = iterated data, F6 = trailer. ; bbbb is no of bytes, dd...cc ; cc is checksum = -(tt+bb+bb+dd+...dd) ; Header record is: ; F0bbbbnnss...ssaappmmllllhhhhcc ; where: ; nn is length of module name ; ss is nn hex-encoded ASCII bytes of name ; aa is address size, 16 or 32 bits ; pp is processor ID, always written as 00 (unknown) ; mm is module type: 02 = no transfer address, 03 = transfer address ; llll is low load address (LOBOUND+ADDVAL lo word) ; hhhh is high load address (HIBOUND+ADDVAL hi word) H.MOSTEK: BCS MOSTKX ; (RETURN) ; No header if APPENDing MOVB #360,MOSTYP ; Record type is F0 ADD #6,%0 ; Put that and length in later MOV %4,%1 ; Load module name length CALL PUTBYT ; Output as nn TST %4 ; Was there a name? BEQ 20$ ; No, bypass copy 10$: MOVB (%3)+,%1 ; Yes, get ASCII byte CALL PUTBYT ; Output SOB %4,10$ ; whole of name 20$: MOVB MODE,%1 ; Get address size CMPB %1,#16. ; If not 16. BEQ 30$ MOV #32.,%1 ; Make it 32. (only other option) 30$: CALL PUTBYT ; Output address size CLR %1 ; Don't know processor CALL PUTBYT ; so say 00 TST (%1)+ ; Type 2 TSTB PART ; if partial BNE 40$ INC %1 ; Else type 3 40$: CALL PUTBYT ; Output file type CALL ADD1ST ; Get low load address + ADDVAL CALL PUTWRD ; Output 4 lo digits ADD COUNT,%1 ; Hi is low + count DEC %1 ; - 1 CALL PUTWRD ; Output that ; BR E.MOSTEK ; End record and return E.MOSTEK: ; End of record PUSH %0 ; Save end pointer MOV #RECORD,%0 ; Point to start of record MOVB MOSTYP,%1 ; For record type CALL PUTBYT ; Store that MOV @SP,%1 ; Compute no of characters in record SUB #RECORD+4,%1 ; Less ttbbbb, but including checksum ASR %1 ; Convert to bytes CALL PUTWRD ; Put it in POP %0 ; Restore pointer to checksum NEG CSUM ; Negate it CALLR ENDCSM ; Append to record, output and return ; Start of record. Do an enumerated type, i.e. byte-by-byte, unless all the ; %5 (>5) bytes from here on have the same value, when a simple iterated ; block is done instead. S.MOSTEK: ; Start record MOVB #362,MOSTYP ; Assume "enumerated" type ADD #6,%0 ; Type and count done later CALL PUT32 ; Output 16- or 32-bit address according to MODE CMP %5,#5 ; Would it be shorter to do an iterated record BLOS MOSTKX ; taking 5 bytes? Not if <= 5 left to do MOV 2(SP),%2 ; Yes, get offsetted address of upcoming byte MOVB MEMORY(%2),%1 ; Get byte value PUSH %5 ; Save count DEC %5 ; Don't need to check first 20$: ADD STEP,%2 ; Address next byte CMPB MEMORY(%2),%1 ; Same as first? BNE 30$ ; No, don't use iterated record SOB %5,20$ ; Yes, keep checking ; If we reach here, all bytes were the same. Output iterated record as: ; nnnn0001dd ; where: ; nnnn is the repeat count, from %5 ; 00 indicates no inner iteration blocks ; 01 indicates 1 byte in repeat pattern MOVB #364,MOSTYP ; Note record type is F4, not F2 POP %1 ; Get count CALL PUTWRD ; Output word DEC %1 ; Include repeat count-1 ADD %1,BCOUNT ; in total file byte count MOV #0001,%1 ; Output inner block flag and count CALL PUTWRD ; Update real and offsetted addresses and set %5 = 1, so following call of ; B.MOSTEK will show the data byte and complete the record normally. MOV %2,%1 ; Copy new offsetted address SUB 2(SP),%1 ; Find how many STEPs we changed by INCR34 %1 ; and so update real address MOV %2,2(SP) ; Update offsetted address PUSH #1 ; Just one last byte of record to do 30$: POP %5 ; Get (revised) count from stack MOSTKX: RETURN ; Go for byte output B.MOSTEK=PUTBYT ; Byte output is just a that T.MOSTEK: ; File trailer MOVB #366,MOSTYP ; Record type is F6 ADD #6,%0 ; Skip that and byte count as usual PUSH %1 ; See if we have a transfer address BIS %2,(SP)+ BEQ E.MOSTEK ; Not if zero, just end record CALL PUT32 ; We do, output 16- or 32- bits as reqd by MODE BR E.MOSTEK ; End record .PAGE .SBTTL WHITESMITH'S V2.1 LINKER OUTPUT (XEQ. FILE) ; File consists of single-byte, fixed-length records, written by HEX as: ; ; bytes value function ; ----- ----- -------- ; 1 231 ident byte, always 99H ; 2 220 configuration byte: ; bit3: 0 = ints are 2 bytes, 1 = 4 bytes ; bit4: 1 = ints stored lsb first ; bit 7: 1 = no relocation information supplied ; 3-4 0 size of symbol table (0=none) ; Next have 6 ints, 2- or 4-bytes according to MODE=16 or 32. ; int1 COUNT number of text (program code) bytes ; int2 0 number of data bytes (0=none) ; int3 0 no of bss bytes (unitialised variables) (0=none) ; int4 0 size of stack+heap (0=none) ; int5 LOBOUND text area start address ; int6 0 data area start address (0=none) ; rest ddd ... output data FROM ... TO ; ; See READ for full meaning of those entries which are dummies here. Note that ; there is no way for HEX to distinguish between text and data, so everything ; is taken as text. ; ; CAUTION: Whitesmiths set the file record size to 1, but write to every byte ; in the file, whereas since FCS forces records to start at even addresses, ; it only writes to every other byte in these circumstances. Since we are in ; locate mode, it is quite possible to write into the gaps in the same way, ; but this may not work on later releases of RSX-11M (OK for V4.0). H.WHITESMITHS: MOV #2,%5 ; Always write data bytes in pairs MOV %5,WIDTH MOV (PC)+,@%0 ; First byte is .BYTE 231,220 ; 99H, and second configuration byte, as above MOV MODE,%4 ; Clear flag on MODE SUB #16.,%4 ; if 16-bits, set <>0 if 32 BEQ 10$ ; Leave bit 3 clear if 16 bits BIS (PC)+,@%0 ; Else if 24 or 32 bits .BYTE 0,^B1000 ; then set it 10$: CALL PUTBLK ; Output the pair CLR @%0 ; No symbols CALL PUTBLK MOV COUNT,@%0 ; Total text size COUNT CLR %1 ; 16 bits only CALL 30$ ; Output text size CALL 20$ ; 3 dummy null words CALL 20$ CALL 20$ CALL ADD1ST ; First address is LOBOUND+ADDVAL MOV %2,@%0 ; Copy lo, hi in %1 CALL 30$ ; Output address 20$: CLR @%0 ; Null int CLR %1 30$: CALL PUTBLK ; Output lo word already in buffer TST %4 ; See if there should be a hi one BEQ 40$ ; No, return MOV %1,@%0 ; Yes, store second CALLR PUTBLK ; Output and return 40$: RETURN B.WHITESMITHS=B.OBJECT ; Byte output: MOVB %1,(%0)+ S.WHITESMITHS=NOOP ; No special record start E.WHITESMITHS=PUTBLK ; Finish by outputting "1"-byte (really 2) T.WHITESMITHS=NOOP ; No file trailer .PAGE .SBTTL WRITE PDP-8/IM6100 RIM/BIN FORMAT FILE ; File consists of binary records of byte pairs in the form: ; tthhhhhh 00llllll ; ; tt = record type: 10 = leader, 01 = address, 00 = data ; ; RIM (Read-In-Mode) has addresses prefixing each byte pair. ; BIN has addresses only at beginnings of records. .ENABL LSB H.RIM: ; Headers H.BIN: CALL EVENWIDTH ; Make sure widths are even BCS 30$ ; (RETURN) ; No leader if APPENDING T.RIM: ; Trailer = header, T.BIN: MOV #50.,%4 ; are just 5" 10$: MOVB #200,(%0)+ ; of track 7 set SOB %4,10$ 12$: CALLR PUTREC ; Output and return S.RIM=NOOP ; No special start for RIM records B.RIM: BIT #1,RECBYT ; First byte of a pair? BEQ 15$ ; No, second, go output value MOVB %1,RIMWRD+1 ; Yes, just save it for second MOV %3,%1 ; Get address lo word ; CALLR S.BIN ; Required to prefix each byte pair S.BIN: ASR %1 ; Output address, halved for words BIC #^C^B111111111111,%1 ; Forgetting values > 12 bits BIS #10000,%1 ; Set address flag BR 20$ ; Go store it B.BIN: BIT #1,RECBYT ; First byte of a pair? BEQ 15$ ; No, second, have a word to output MOVB %1,RIMWRD+1 ; Yes, first, just save it RETURN ; Until we have other half 15$: CLRB RIMWRD ; Now have lo byte, clear mask BIS RIMWRD,%1 ; OR in hi BIC #^C^B111111111111,%1 ; Select lo 12 bits only 20$: PUSH %1 ; Save word BIC #^C^B111111,@SP ; For lo 6 bits ASL %1 ; Shift up 2 bits to align hi 6+flag ASL %1 SWAB %1 ; Get hi byte MOVB %1,(%0)+ ; Store it MOVB (SP)+,(%0)+ ; followed by lo 6 bits 30$: RETURN ; and exit E.RIM: ROR %1 ; See if we ended on an odd byte (%1 is odd) BCC 12$ ; (PUTREC) ; Output record now if not CLR %1 ; Load a dummy null byte CALL B.RIM ; and output that first BR 12$ E.BIN: ROR %1 ; Check for odd byte count BCC 12$ CLR %1 ; Else need a padding null again CALL B.BIN BR 12$ .DSABL LSB .PAGE .SBTTL WRITE HEX-CHAR AND OCTAL-CHAR FORMATS ; ^B$Abbbb, ; aaaa-ddxddxddx...ddx ; ^C$Sssss, ; where: ; bbbb is PROM base address ; aaaa is load address ; dd is data ; x is separator, in SEPTOR, usually space, ', or % ; ssss is checksum = dd+dd+... ; ; See READ, and manual for discussion of the difference between addresses ; bbbb and aaaa. .ENABL LSB H.OCTAL: ; File start MOV #200$,PUTBSB ; Put byte routine is local 200$ MOV #220$,PUTWSB ; Put word is 220$ MOV #220$,PUTASB ; Put address is 20$, CMP MODE,#16. ; if 16 bits BEQ 1$ MOV #240$,PUTASB ; Else 240$ BR 1$ ; Join common code H.HEX: MOV #PUTHX2,PUTBSB ; Put byte routine is PUTHX2 MOV #PUTHX4,PUTWSB ; Put word is PUTHX4 MOV #PUTHXL,PUTASB ; Put address may need more, according to MODE 1$: MOVB #'-,ADREND ; Set up flag char for aaaa- CMPB SEPTOR,#'- ; Use -, unless that's the separator BNE 2$ MOVB #'=,ADREND ; When use = instead ; If there is one, output program name as first (comment) line. 2$: TST %4 ; Any name? BEQ 6$ ; No, just return 3$: MOVB (%3)+,(%0)+ ; Copy bytes SOB %4,3$ BR 40$ ; (PUTREC) ; Output name and return S.HEX: ; Record start S.OCTAL:CLR %1 ; Dummy address of 0 MOV (PC)+,%2 ; Start: .BYTE 'B&37,'A ; Control/B and 'A' CALL 100$ ; Output padding null bytes, if necessary, between load address and PROM base. ; %4 = number in first record, WIDTH-%5 = no of nulls (always zero on all but ; first PROM block). MOV WIDTH,%2 ; Compute no of padding bytes required SUB %5,%2 BEQ 6$ ; 0 means at base of PROM already MOV STEP,%1 ; <>0. Get STEP .IF DF M$$EIS MUL %2,%1 ; Step back address by no of pad bytes * STEP .IFF PUSH %0 MOV %2,%0 ; Step back address CALL $MUL ; by number of padding bytes * STEP POP %0 .ENDC DECR34 %1 5$: CLR %1 ; Store nulls INC COUNT ; Include in total INC RECBYT ; and record counters CALL B.HEX ; just like standard output would INCR34 STEP ; Advance address SOB %2,5$ ; Repeat until required no done 6$: RETURN ; and return B.HEX: ; Byte write B.OCTAL:CMP %0,FDB+F.NRBD+2 ; At start of line? BNE 7$ ; No, just output byte PUSH %1 ; Yes, save byte PUSH %2 ; and offsetted address MOV %3,%1 ; Get real address MOV %4,%2 CALL @PUTASB ; Output in required radix MOVB ADREND,(%0)+ ; with end mark, '-' or '=' POP %2 ; Restore offsetted address POP %1 ; and byte value 7$: ADD %1,CSUM ; Add to checksum CALL @PUTBSB ; Output byte MOVB SEPTOR,(%0)+ ; and terminator BIT #17,RECBYT ; 16 bytes on this line? BEQ 40$ ; Write out line if so 20$: RETURN ; More this line if not E.HEX: ; Record end E.OCTAL:CMP %0,FDB+F.NRBD+2 ; Just done newline? BEQ 25$ ; Yes, line currently blank CALL PUTREC ; No, write out last line 25$: MOV CSUM,%1 ; Get checksum PUSH %2 ; Save %2 MOV (PC)+,%2 ; Start: .BYTE 'C&37,'S ; Control/C and 'S' CALL 100$ POP %2 ; Restore %2 ; Separate blocks by line of 20 spaces, then ^S^Q so Data I/O programmer ; doesn't concatenate blocks. ^S turns off (some) tape readers, following ; ^Q prevents clagging up VT100 keyboard. MOV #20.,%5 ; Load counter MOV #RECORD,%0 ; Address buffer 30$: MOVB #SPACE,(%0)+ ; Output spaces SOB %5,30$ MOVB #'S&37,(%0)+ ; Then control/S MOVB #'Q&37,(%0)+ ; and control Q 40$: CALLR PUTREC ; Output record and start again T.HEX=NOOP ; No file trailer T.OCTAL=NOOP ; Output header or checksum: control code, $, A/S, hex/octal no, and comma: 100$: MOVB %2,(%0)+ ; Output control code MOVB #'$,(%0)+ ; '$', SWAB %2 MOVB %2,(%0)+ ; and A/S CALL @PUTWSB ; Output number -- hex or octal MOVB #',,(%0)+ ; Complete with comma BR 40$ ; (PUTREC) ; Output record and return ; Output octal byte/word, using Syslib routines 200$: PUSH %2 ; Output byte: save %2 MOV SP,%2 ; Non-z-sup indicator for $CBTMG CALL $CBTMG ; Store byte BR 230$ ; Restore %2 and return 220$: PUSH %2 ; Output word: save %2 SETNZ %2 ; Non-z-sup indicator CALL $CBOMG ; Output octal word 230$: POP %2 ; Restore %2 RETURN ; and return ; Output zero-suppressed 24- or 32-bit octal address from %1/%2 (destroyed). 240$: CMP MODE,#24. ; 24-bits? BGT 250$ ; No, 32 BIC #^C377,%2 ; Yes, lose bits 25-31 250$: CLR -(SP) ; Clear end-of-number flag 260$: PUSH %1 ; Push lo word onto LIFO stack BIC #^C7,@SP ; Bits 0-2 only ADD #'0,@SP ; Make into an ASCII digit (clears carry .REPT 3 ; for unsigned) shift %2/%1 3 bits right ROR %1 ROR %2 .ENDR BNE 260$ ; Repeat while either word TST %1 BNE 260$ ; <> 0 270$: MOVB (SP)+,(%0)+ ; Done: pop bytes off stack into record BNE 270$ ; Until flag is met DEC %0 ; Lose flag RETURN ; and return .DSABL LSB .PAGE .SBTTL WRITE TCI FORMAT ; ; @aaaadddd...dd ; where: ; aaaa = address ; dd...dd is data bytes ; .IF NDF TCI NOTIMP TCI .IFF H.TCI=NOOP ; No special file start S.TCI: ; Record start MOVB #'@,(%0)+ ; Start line with @ CALLR PUTHX4 ; and address B.TCI=PUTHE2 ; Byte write done directly E.TCI=PUTREC ; Record end, just output T.TCI=NOOP ; No file trailer .ENDC .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 H.SIRA=NOOP ; No special file start S.SIRA: ; Record start MOVB #1,(%0)+ ; Store 1 flag CALL BINWRD ; Output address, adding to c/sum MOVB SIRTYP,%1 ; Load type (preset 0 for data blocks) CALL BINBYT ; Output type CMPB (%0)+,(%0)+ ; Bypass byte count filled in later RETURN ; Return B.SIRA=BINBYT ; Byte write T.SIRA: ; File trailer INCB SIRTYP ; 1 is no autostart, change 0 to 1 BIS %1,%2 ; if %1/%2=0 BEQ 30$ INCB SIRTYP ; 2 is autostart 30$: CALL S.SIRA ; Start record CLR %1 ; No data bytes ; CALLR E.SIRA ; End as usual E.SIRA: ; Record end PUSH %0 ; Save end of record pointer MOV #RECORD+4,%0 ; address byte count point CALL BINWRD ; Write byte count there POP %0 ; Restore end of record pointer MOVB CSUM,(%0)+ ; Append checksum byte to end of record CALLR PUTREC ; Output, and return .PAGE .SBTTL DEC ABSOLUTE BINARY FORMAT ; As MACRO assembly with .ENABL ABS, or /EN:ABS. See READ for further notes. ; ; Records are just: ; aaaadddd... ; where: ; aaaa = load/start address ; dd... = data bytes H.OBJECT=NOOP ; No special file start S.OBJECT: ; Record start MOVB %1,(%0)+ ; Copy address lo SWAB %1 ; Swap for hi ; CALLR B.OBJECT ; Output and return B.OBJECT: ; Byte write MOVB %1,(%0)+ ; Copy byte RETURN ; and return E.OBJECT=PUTREC ; Record end -- just write it out T.OBJECT: ; File trailer TST %1 ; is just address if <>0 (start address) BNE 10$ INC %1 ; or 1 if not 10$: CALL S.OBJECT ; Write address into record CALLR PUTREC ; Then write out record .PAGE .SBTTL WRITE PDP-11 PAPER-TAPE ABSOLUTE LOADER FORMAT FILE ; 10bbaadddd...dc ; where (bytes): ; bb = byte count -- lo/hi ; aa = address -- lo/hi ; t = type: 0=data, 1=EOF, 2=autostart ; dd...dd = data bytes ; c = checksum, -(1+b+b+a+a+d+...d) H.ABSOLUTE=NOOP ; No special file start S.ABSOLUTE: ; Record start MOV #1,(%0)+ ; Store flag 1, + null TST (%0)+ ; Bypass byte count filled in later CALL BINWRD ; Output address (lo word), adding to c/sum RETURN ; Return B.ABSOLUTE=BINBYT ; Byte write T.ABSOLUTE: ; File trailer BIS %1,%2 ; OR words BNE 30$ ; <>0 is address given INC %1 ; 0 is none, flagged with 1 30$: CALL S.ABSOLUTE ; Start record CLR %1 ; No data bytes ; CALLR E.ABSOLUTE ; End as usual E.ABSOLUTE: ; Record end PUSH %0 ; Save end of record pointer ADD #6,%1 ; Count includes 10bbaa MOV #RECORD+2,%0 ; Address byte count point CALL BINWRD ; Write it there POP %0 ; Restore end of record pointer MOVB CSUM,@%0 ; Get checksum COMB (%0)+ ; Add 1, and negate (=complement) so total is 0 CALLR PUTREC ; Output, and return .PAGE .SBTTL TASK FILE FORMAT ; ; See READ for full details. First block is file header containing: ; ; L$BTSK (0-3) task name, radix-50 ; L$BSA (10) lo load addr ; L$BHGV (12) hi load addr ; L$BXFR (350) start address ; L$BHRB (356) offset to task data block from this block ; ; This can be read back in by HFE (or SSE) but some of the restrictions of ; TKB are removed, so it is no longer TKB compatible. No second label block ; is written, so L$BHRB=0. .IF NDF TASK NOTIMP TASK .IFF .MCALL LBLDF$ LBLDF$ ; Define task header block offsets .PSECT WRITE I,RO H.TASK: ; Write label block CALL S.TASK ; Clear block and point to start BIS %1,%2 ; See if there is a transfer address BNE 5$ ; OK if so MOV #1,%1 ; Make it 1 if not 5$: MOV %1,L$BXFR(%0) ; Store transfer address, or 1 CALL ADD1ST ; Get lo load address + ADDVAL MOV %1,L$BSA(%0) ; lo word only MOV HIBOUND,L$BHGV(%0) ; and end ADD ADDVAL,L$BHGV(%0) INC L$BHRB(%0) ; Data starts in next block (no 2nd label block) MOV %0,%4 ; Copy pointer to file header MOV %3,%0 ; Load pointer to program name MOV #2,%3 ; (Even if none = spaces, becomes 0/0) 10$: SETNZ %1 ; Allow .'s in program name CALL $CAT5B ; on converting ASCII to Radix-50 BCS 20$ ; Ignore if it failed (non Radix-50 char) MOV %1,(%4)+ ; Store first word SOB %3,10$ ; Repeat for second 20$: CALLR PUTBLK ; Label block complete. Write it and return E.TASK=PUTBLK ; Record end -- just write out block ; Clear file block, to make it easier to understand DMP's. S.TASK: ; Record start MOV #512.,%5 ; All blocks are 512. bytes MOV #256.,%1 ; Load block size in words 10$: CLR (%0)+ ; Clear 2 bytes at a time SOB %1,10$ ; Until done, leaving %0 addressing block SUB %5,%0 ; Point back to start of block RETURN ; and return B.TASK=B.OBJECT ; Byte write is simple MOVB T.TASK=NOOP ; No special file trailer .ENDC .PAGE .PSECT PURE D,RO ; Set up table of format processing routines and default/maximum widths. .MACRO ENTRY FORMAT DWIDTH MWIDTH .WORD H.'FORMAT,S.'FORMAT,B.'FORMAT,E.'FORMAT,T.'FORMAT .WORD DWIDTH,MWIDTH .ENDM ENTRY TABLE: ; Format def width max width .IF DF TCP ;23MAR4 ENTRY INTEL 32. 250. ;23MAR4 ENTRY MOTOROLA 32. 252. ;23MAR4 ENTRY ROCKWELL 32. 252. ;23MAR4 ENTRY RCA 32. 169. ;23MAR4 ENTRY TEKHEX 32. 250. ;23MAR4 ENTRY EXTENDED 32. 250. ;23MAR4 ENTRY TEXAS 32. 200. ;23MAR4 ENTRY MOSTEK 32. 250. ;23MAR4 ENTRY WHITESMITHS 100000!1 100000!1 ; WIDTH = 1 always ;**-8 ENTRY RIM 64. 254. ENTRY BIN 128. 254. ENTRY HEX 1024. 16384. ; WIDTH = PROM size ENTRY OCTAL 1024. 16384. ; ditto ENTRY TCI 32. 253. ;23MAR4 .IFF ;23MAR4 ENTRY INTEL 16. 250. ;23MAR4 ENTRY MOTOROLA 16. 252. ;23MAR4 ENTRY ROCKWELL 16. 252. ;23MAR4 ENTRY RCA 16. 169. ;23MAR4 ENTRY TEKHEX 16. 250. ;23MAR4 ENTRY EXTENDED 16. 250. ;23MAR4 ENTRY TEXAS 16. 200. ;23MAR4 ENTRY MOSTEK 16. 250. ;23MAR4 ENTRY WHITESMITHS 100000!1 100000!1 ; WIDTH = 1 always ;23MAR4 ENTRY RIM 64. 254. ;23MAR4 ENTRY BIN 128. 254. ;23MAR4 ENTRY HEX 1024. 16384. ; WIDTH = PROM size ;23MAR4 ENTRY OCTAL 1024. 16384. ; ditto ;23MAR4 ENTRY TCI 16. 253. .ENDC ;23MAR4 ENTRY SIRA 64. 505. ENTRY OBJECT 64. 510. ENTRY ABSOLUTE 64. 510. ENTRY TASK 100000!512. 100000!512. ; WIDTH = 512. always KEY WIDTH DEFM IAP DEFM BDW ABOBLK: .ASCII "// No transfer address" ; TekHex abort block ABOLEN=.-ABOBLK .EVEN .PSECT DATA D,RW ; Output routines for this format: HSUB: .BLKW 1 ; Start of file SSUB: .BLKW 1 ; Start of record output BSUB: .BLKW 1 ; Byte-by-byte output ESUB: .BLKW 1 ; End-of-record output TSUB: .BLKW 1 ; End of file output ADDVAL: .BLKL ; Value to be added to addresses output RECBYT: .BLKW 1 ; Bytes in current record BCOUNT: .BLKW 1 ; Grand total of bytes output CSUM: .BLKW 1 ; Current checksum TOTCSM: .BLKW 1 ; Sum of bytes values written RETSP: .BLKW 1 ; Entry SP, for abnormal exits WIDTH: .BLKW 1 ; Bytes/output record PART: .BLKB 1 ; No trailer flag ERRFLG: .BLKB 1 ; Error flag .EVEN ; Remaining space used for different purposes by different output routines ; First word is always cleared before header output. COMMON: ; Rockwell format RECCNT: .BLKW 1 ; Records output count ; PROM formats .=COMMON PUTBSB: .BLKW 1 ; Byte output routine PUTWSB: .BLKW 1 ; Word output routine PUTASB: .BLKW 1 ; Address output routine ADREND: .BLKB 1 ; aaaa address end flag byte ('-' or '=') .EVEN ; RIM and BIN formats .=COMMON RIMWRD: .BLKW 1 ; Temporary storage of hi byte ; TekHex format .=COMMON HDCSUM: .BLKW 1 ; Temporary storage of header checksum ; Extended TekHex format .=COMMON ETKTYP: .BLKB 1 ; Record type ; Sira format .=COMMON SIRTYP: .BLKB 1 ; Record type ; Mostek format .=COMMON MOSTYP: .BLKB 1 ; Record type .END