1 " ***********************************************************
  2 " *                                                         *
  3 " * Copyright, (C) Honeywell Bull Inc., 1987                *
  4 " *                                                         *
  5 " * Copyright, (C) Honeywell Information Systems Inc., 1982 *
  6 " *                                                         *
  7 " ***********************************************************
  8 
  9 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
 10 "                                                                     "
 11 "         BOOTLOAD_LOADER: Subroutine to read                         "
 12 "         collection 0.5 (tape fw) or collection 1.                   "
 13 "                                                                     "
 14 "         tsx2      bootload_loader$load_collection                   "
 15 "         tsx2      bootload_loader$skip_collection                   "
 16 "         tsx2      bootload_loader$init                              "
 17 "                                                                     "
 18 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
 19 
 20 " HISTORY COMMENTS:
 21 "  1) change(85-09-09,Farley), approve(85-09-09,MCR6979),
 22 "     audit(86-03-05,GDixon), install(86-03-21,MR12.0-1033):
 23 "      Support IMU and FIPS.
 24 "  2) change(87-03-12,Farley), approve(87-07-06,MCR7717),
 25 "     audit(87-07-15,Fawcett), install(87-07-17,MR12.1-1043):
 26 "     Corrected adjust_tape_device routine to only alter the IDCW if dealing
 27 "     with a non-cold MPC.  Also added reset&mask to POF retry.
 28 "  3) change(87-07-23,Farley), approve(87-07-23,PBF7717),
 29 "     audit(87-07-24,Fawcett), install(87-07-28,MR12.1-1047):
 30 "     Changed POF retry to only unwedge the tape controller one time.  Found
 31 "     that more than once causes the IMU to fault..
 32 "                                                      END HISTORY COMMENTS
 33 
 34 
 35 " Created  10/02/80, W. Olin Sibert, from bootstrap1 and assorted oddments
 36 " Modified 11/12/80, WOS, to add automatic tape firmware finder/loader.
 37 " Modified 12/16/80, WOS, to construct SDWs for both ADP and Level 68, and
 38 "  to eliminate check_overflow, on the assumption that the MST checker
 39 "  should do that checking instead.
 40 " Modified 04/21/81, Chris Jones for simplified I/O
 41 " Modified 9/83, Keith Loepere for adp and paged wired segs.
 42 " Modified 12/83, Keith Loepere for breakpoint_page support.
 43 " Modified June-July 1984 by Paul Farley for IMU support.
 44 " Modified 7/84, Keith Loepere to not clobber bkpt page.
 45 " Modified 5/85, Chris Jones to calculate position of trailer correctly for
 46 "  padded records.
 47 
 48           name      bootload_loader
 49 
 50           include   bootload_equs
 51           include   bootload_cpu_macros
 52           include   coll0_segnos
 53           include   hc_definitions_seg
 54           include   hc_linkage_seg
 55           include_nolist      iom_word_macros
 56           include_nolist      make_data_macros
 57           include   mstr
 58           include   sdw_info
 59           include   slt
 60           include   slte
 61           include   system_types
 62           include_nolist      tape_io_commands
 63           include   unpaged_page_tables
 64 " ^L
 65           equ       prb.ctl,0
 66           equ       prb.data,1
 67           equ       prb.nameptr,1041
 68           equ       prb.hdr,1042
 69 
 70 
 71           Bentry    init
 72 init:
 73           Bpush
 74 
 75           absa_au   prb|prb.data
 76           stca      read_tape_ddcw,70
 77           neg       0                   " adjust prb pointer
 78           sba       mst_label.head,du   " correct for MSTRH
 79           asa       prb|prb.ctl
 80 
 81 " Note (ahem) that the following code depends on the fact that:
 82 "  1. data_bits_used and data_bit_len are in the same word (upper and lower),
 83 "  2. they are both multiples of 36 so the division leaves word oriented values
 84 "     in qu and ql, and
 85 "  3. the upper half of ndata is the amount of real data, BUT
 86 "     the lower half is the actual amount of data read (including the pad)
 87 
 88           ldq       prb|prb.data+mstr_header.data_bits_used
 89           div       36,dl
 90           stq       ndata
 91 
 92           lda       prb|prb.data+mstr_header_size+mstr_trailer.tot_rec_word,ql
 93                                         " current record
 94           ada       1,dl
 95           sta       exprec
 96           stz       unwedged            " reset flag word
 97           Breturn
 98 
 99 
100           segdef    finish
101 
102 finish:   eax7      3*2
103 setclr:   epp       seg,clptrs,x7*
104           epbp      seg2,seg|0
105           spri6     seg2|hc_linkage_seg.next_free_ptr
106           eax7      -2,x7               " on to next
107           tpl       setclr
108 
109           epp       seg,cdptr,*
110           epbp      seg2,seg|0
111           spri6     seg2|definitions.next_free_ptr
112 
113           tra       0,x2
114 "^L
115           Bentry    skip_collection
116 
117 skip_collection:
118           Bpush
119 
120           tsx2      adjust_tape_device
121 
122 skip_next:
123           tsx2      readcw              " Look at next control word
124           tra       something_to_skip   " data
125           tsx0      bootload_error$bad_cw
126 
127 load.cmark:
128           tsx2      skip                " skip the collection mark
129           arg       k1
130           Breturn
131 
132 something_to_skip:                      " First skip the SLTE
133           tsx2      skip                " grab the header
134           arg       cw
135 
136           tsx2      readcw              " Now read the CW
137           tsx0      bootload_error$bad_cw
138           tra       skip.data
139           tsx0      bootload_error$bad_cw
140 
141 skip.data:tsx2      skip
142           arg       cw
143           tra       skip_next           " and again
144 " ^L
145           Bentry    load_collection
146 
147 load_collection:
148           Bpush
149 
150           tsx2      adjust_tape_device
151 
152 load.text:tsx2      read_header         " read the header
153           tra       load.cmark          " we are done
154 
155           lda       prb|prb.hdr-1+slte.link_sect_word
156                                         " make sure this is the text
157           cana      slte.link_sect+slte.defs,dl
158           tnz       bootload_error$bad_sequence
159 
160           tsx2      bootload_slt_manager$build_entry        " build the SLTE
161           arg       hcw
162           arg       hdrp,*
163           tra       bootload_error$too_many_segs
164 
165           spri1     segptr                        " save pointer to it
166           spri6     lastsltptr
167 
168 "         check to see if this is an 'interesting' segment.
169 
170           ldx7      seg|slte.names_ptr
171           lda       nt|segnam.name,x7   get first 2 words of first name.
172           ldq       nt|segnam.name+1,x7 "
173           eax6      interesting_names   " x6 -> table of interesting names
174           rpt       no_interesting_names,4,tze    search the table
175           cmpaq     0,x6                compare first 8 characters
176           ttn       load_segment        skip if no compare
177 
178           lxl0      seg|slte.segno                get segment number again
179           stx0      -1,x6*              store segment number for later use
180 
181 "         Make SDW for segment and load it.
182 
183 load_segment:
184           tsx7      allocate            routine to allocate core
185 
186           tsx2      read                " read the segment from tape
187           arg       segptr,*
188           arg       cw                  and store it in
189 
190           tsx7      setaccess           now set access on loaded segment
191 
192           lda       prb|prb.hdr-1+slte.link_provided_word
193           cana      slte.link_provided,dl " linkage to read?
194           tze       load.text           " no
195 " ^L
196 "         Process linkage segment.
197 
198           tsx2      read_header
199           tsx0      bootload_error$bad_sequence
200 
201           lda       prb|prb.hdr+slte.link_sect_word-1 test for linkage segment
202           cana      slte.link_sect,dl   ..
203           tze       bootload_error$bad_sequence
204 
205           eax7      lastsltptr,*        get index of text SLT entry
206           eax0      0                   X0 will contain index to combined lkg ptr
207           lda       slt|slte.init_seg_word,x7     init_seg?
208           cana      slte.init_seg,dl    ..
209           tze       *+2                 if so,
210           eax0      4,x0                increment index
211           lda       slt|slte.link_sect_wired_word,x7  wired linkage?
212           cana      slte.link_sect_wired,dl  ..
213           tze       *+2                 if so,
214           eax0      2,0                 increment index
215 
216           epp       seg,clptrs,x0*      pr6 -> correct linkage segment free
217                                         " word
218           spri6     tempptr             " save it
219           lxl1      slt|slte.segno,x7   X1 contains segment number of text segment
220           sprp      seg,bootload_info$lot_ptr,*x1 set lot entry
221 
222           lxl6      cw                  length of linkage in X6
223           adx6      1,du                add one to size of this linkage
224           anx6      =o777776,du         rounded up to next even
225           asx6      clptrs+1,x0         " update pointer
226 
227           tsx2      read                " read in the linkage
228           arg       tempptr,*
229           arg       cw
230 
231           ldx7      segptr              segment number of text in X7
232           epp       seg,tempptr,*       linkage header
233           stx7      seg|7               set text pointer in linkage header
234 " ^L
235 "         Process definitions segment.
236 
237           tsx2      read_header
238           tsx0      bootload_error$bad_sequence
239 
240           lda       prb|prb.hdr+slte.defs_word-1 test for defs segment
241           cana      slte.defs,dl        ..
242           tze       bootload_error$bad_sequence
243 
244           epbp      seg,cdptr,*         get ptr to base of defs
245           lda       cw                  get length of defs section
246           ana       =o777777,dl         ..
247           ora       cdptr+1             insert offset of place for defs
248           ldx7      segptr              segment number of text in X7
249           sta       seg|0,x7            add entry to definitions offset table
250 
251           epp       seg,cdptr,*         pr6 -> place for defs
252           spri6     tempptr,*           " save it in linkage
253           spri6     tempptr             " and for reading
254           lxl6      cw                  get length of defs in X6
255           asx6      cdptr+1             set pointer for next time
256 
257           tsx2      read                " read the defs
258           arg       tempptr,*
259           arg       cw
260 
261           tra       load.text           and loop
262 " ^L
263 "         read in segment header information
264 
265 read_header:
266           Bpush
267           tsx2      readcw              " read the control word
268           tra       rh.header
269           tsx0      bootload_error$bad_cw
270 
271           Breturn
272 
273 rh.header:lda       cw                  " save away CW
274           sta       hcw
275           tsx2      read                  " read in the logical segment header.
276           arg       hdrp,*
277           arg       hcw                 number of words wanted
278 
279           tsx2      readcw                " get cw of the segment
280           tsx0      bootload_error$bad_cw
281           tra       read_header_returns
282           tsx0      bootload_error$bad_cw
283 
284 read_header_returns:
285           Breturn   1
286 
287 
288 readcw:   Bpush
289           tsx2      read
290           arg       cw
291           arg       k1
292           ldx7      cw                  " get CW type
293           tze       rcw.0               " type 0
294           ersx7     cw                  " clear the word
295           cmpx7     1,du
296           tze       rcw.1               " type 1
297           cmpx7     2,du                " type 2
298           tnz       bootload_error$bad_cw
299           Breturn   2
300 rcw.1:    Breturn   1
301 rcw.0:    Breturn   0
302 " ^L
303           segdef    segptr
304 
305           even
306 segptr:   its       -1,1
307 tempptr:  its       -1,1
308 lastsltptr:
309           its       -1,1
310 clptrs:   its       -1,hc_linkage_seg.free_area   active_sup_linkage
311           its       -1,hc_linkage_seg.free_area   wired_sup_linkage
312           its       -1,hc_linkage_seg.free_area   active_init_linkage
313           its       -1,hc_linkage_seg.free_area   wired_init_linkage
314 cdptr:    its       -1,definitions.first_free_word  definitions_
315 
316 hdrp:     itp       prb,prb.hdr-1
317 
318 si:       bss       ,sdw_info_size
319 hcw:      dec       0                   control word for logical header
320 cw:       dec       0                   control word
321 k1:       dec       1                   " constant one
322 bit_count_mask:
323           oct       000077777777        " mask for slte.bit_count
324 " ^L
325           macro     int_name
326           maclist   object,save
327           aci       @&1@,8
328           zero      &l2,0
329           arg       &3
330           maclist   restore
331 &end
332 
333 "         Table of 'interesting' names.
334 
335           even
336 interesting_names:
337 
338           int_name  bootload,bootload_1,bootload_info$bootload_1_ptr
339 
340           int_name  lot,lot,bootload_info$lot_ptr
341 
342           int_name  as_linka,as_linkage,clptrs
343           int_name  ws_linka,ws_linkage,clptrs+2
344           int_name  ai_linka,ai_linkage,clptrs+4
345           int_name  wi_linka,wi_linkage,clptrs+6
346 
347           int_name  definiti,definitions_,cdptr
348 
349           int_name  sys_boot,sys_boot_info,bootload_info$sys_boot_info_ptr
350 
351           equ       no_interesting_names,(*-interesting_names)/4
352 " ^L
353 "         ALLOCATE
354 "
355 "         This subroutine is called (via TSX7) to allocate storage for the segment
356 "         described by the SLTE currently in the headersegment buffer. An sdw_info
357 "         is constructed (si), and bootload_dseg$make_sdw is called to fabricate
358 "         the SDW. The SDW is initially constructed with RW access, so the segment
359 "         contents can be read in. After the segment is read in, the set_access
360 "         entrypoint is called, the SDW is refabricated with the appropriate access,
361 "         and is stored into the DSEG again. The sdw_info is constructed in the area
362 "         called "si".
363 "
364 "         Segments are allocated according to the following rules. X1 is used
365 "         as a flag to indicate where the segment is to be allocated.
366 "
367 "          * If the segment is zero length on the tape, it gets an all zero
368 "            SDW. This case is handled first.
369 "
370 "          * If the segment is a paged supervisor segment, or any sort of
371 "            init-seg, it is allocated in a contiguous region of high order
372 "            memory, on a 1024 word boundary.  Its page table is put in
373 "            int_unpaged_page_tables.
374 "
375 "          * If the segment is an unpaged supervisor segment, it is allocated
376 "            on a 1024 word boundary starting from the low end of memory.  Its
377 "            page table is put in unpaged_page_tables.
378 "
379 "         If the segment has e access, we add the breakpoint_page as an
380 "         extra page to the end.
381 
382 running_address:
383           dec       0
384 seg_text_length:
385           dec       0
386 
387 allocate:
388           mlr       (),(),fill(000)     " Clear out sdw_info
389           desc9a    0,0
390           desc9a    si,4*sdw_info_size
391 
392           ldq       seg|slte.bit_count_word " Find out how big it is
393           anq       bit_count_mask
394           adq       35,dl               " Round up to a word
395           div       36,dl               " and figure out how many words it is
396 
397           tze       allocate_empty_seg  " If nothing there, give up now
398 
399           adq       1023,dl             " round up to 1024 words
400           anq       =o776000,dl
401           stq       si+sdw_info.bound   " Save the length
402           stq       seg_text_length     " amount to clear (doesn't count bkpt page)
403 
404           lda       slt|slt.free_core_size " See if we've got room
405           sba       si+sdw_info.bound
406           sba       1024,dl             " subtract a page to allow for slop
407           tmi       bootload_error$out_of_main_memory
408 
409           lda       seg|slte.firmware_seg_word
410           cana      slte.firmware_seg,du
411           tnz       allocate_seg_low
412 
413           lda       seg|slte.paged_word " Is seg to be paged?
414           cana      slte.paged,du
415           tnz       allocate_seg_high
416 
417           lda       seg|slte.init_seg_word        " Or is it an init-seg, maybe?
418           cana      slte.init_seg,dl
419           tnz       allocate_seg_high   " Yup, place with paged segments
420 
421 allocate_seg_low:
422           lda       slt|slt.free_core_start       " Get space in low memory
423           sta       running_address               " Save the address
424           ada       si+sdw_info.bound             " Add the length, and adjust the
425           sta       slt|slt.free_core_start       " beginning of free core
426           lda       slt|slt.free_core_size        " Also adjust the size
427           sba       si+sdw_info.bound
428           sta       slt|slt.free_core_size
429           epp       seg2,=its(upt_segno_,0),*
430           tra       have_seg_address
431 
432 allocate_seg_high:
433           lda       slt|slt.free_core_start       " Assume these are already
434           ada       slt|slt.free_core_size        " 1024 word aligned.
435           sba       si+sdw_info.bound             " Adjust down by the seg lth.
436           sta       running_address               " Save the address
437           sba       slt|slt.free_core_start       " and calculate the new size
438           sta       slt|slt.free_core_size        " of free core -- free_core_start
439           epp       seg2,=its(iupt_segno_,0),*
440           tra       have_seg_address              " is unchanged
441 
442 have_seg_address:                                 " pr(seg2) -> upt (unpaged page table)
443           lda       seg|slte.access_word " see if we should add breakpoint_page
444           ana       =o200000,du         " e permission
445           tze       non_bkpt
446           lda       slte.breakpointable,du
447           orsa      seg|slte.breakpointable_word
448           lda       1024,dl             " lengthen for bkpt page
449           asa       si+sdw_info.bound
450           lda       1,dl
451           als       slte.cur_length_shift
452           adla      seg|slte.cur_length_word
453           sta       seg|slte.cur_length_word " record as lengthened
454           lda       1,dl
455           als       slte.max_length_shift
456           adla      seg|slte.max_length_word
457           sta       seg|slte.max_length_word
458 non_bkpt:
459           ldq       seg2|upt.current_length
460           lda       si+sdw_info.bound
461           ars       10                            " # of pages
462           eax3      0,al
463 
464           ada       3,dl
465           ars       1
466           als       1                             " round to next size upt entry
467           ada       seg2|upt.current_length
468           sta       seg2|upt.current_length       " allocated page table
469           cmpa      seg2|upt.max_length
470           tmoz      2,ic
471           tsx0      bootload_error$upt_overflow
472           epp       seg2,seg2|0,ql                " -> upt_entry
473 
474           lda       segptr
475           ars       18                            " segno
476           sta       seg2|upt_entry.segno
477           stz       seg2|upt_entry.size
478           sxl3      seg2|upt_entry.size
479           epp       seg2,seg2|upt_entry.ptws
480           absa_al   seg2|0
481           sta       si+sdw_info.address
482 
483           lda       seg|slte.breakpointable_word
484           cana      slte.breakpointable,du
485           tze       2,ic
486           eax3      -1,x3                         " last ptw is special for bkpt page
487 
488           lda       running_address
489 gen_ptw:
490           tsx2      bootload_dseg$make_core_ptw
491           stq       seg2|0
492           ada       1024,dl
493           epp       seg2,seg2|1
494           eax3      -1,x3
495           tnz       gen_ptw
496 
497           lda       seg|slte.breakpointable_word " add bkpt ptw if necessary
498           cana      slte.breakpointable,du
499           tze       no_add_bkpt_ptw
500           lda       bkpt_absloc,dl
501           tsx2      bootload_dseg$make_core_ptw
502           stq       seg2|0
503 
504 no_add_bkpt_ptw:
505           lda       sdw_info.paged,du
506           orsa      si+sdw_info.flags
507 
508           lda       sdw_info.read+sdw_info.write,du " Set RW access
509           sta       si+sdw_info.access
510 
511           epp       seg,si
512           tsx2      bootload_dseg$make_sdw
513 
514           ldx3      segptr              " segno * 2 in X3
515           adlx3     segptr
516           staq      ds|0,x3             " Ka-ching!
517           lda       bootload_info$system_type
518           cmpa      ADP_SYSTEM,dl
519           tze       2,ic
520           cams      0
521 
522           lda       seg_text_length     " now clear the segment
523           als       2                   " characters
524           epp       seg,segptr,*
525           mlr       (),(pr,rl),fill(0)
526           desc9a    0
527           desc9a    seg|0,al
528           tra       0,x7                " and return
529 
530 
531 allocate_empty_seg:
532           lda       sdw_info.faulted,du " Make the segment empty
533           sta       si+sdw_info.flags   " All the rest of si is still zero
534           ldx3      segptr              " segno * 2 in X3
535           adlx3     segptr
536           stz       ds|0,x3             " Ka-ching!
537           stz       ds|1,x3             " Ka-ching!
538           tra       0,x7                " and return
539 " ^L
540 "         SETACCESS
541 "
542 "         This entry is called to set the proper access for the segment, once it
543 "         has been read in. This is also where the encacheable bit is set.
544 "
545 
546 setaccess:
547           lda       si+sdw_info.flags   " Is it worth it?
548           cana      sdw_info.faulted,du
549           tnz       0,x7                " Nope, SDW is unusable
550 
551           epp       seg,prb|prb.hdr-1   " PR6 -> slte
552 
553           lda       seg|slte.cache_word " Set the encacheability bit
554           ana       slte.cache,du
555           tze       3,ic
556           lda       sdw_info.cache,du
557           orsa      si+sdw_info.flags
558 
559           lda       seg|slte.access_word " Set the correct access
560           arl       slte.access_shift   " To low 4 bits
561           als       36-4                " Now to high 4 bits
562           sta       si+sdw_info.access  " and store it
563 
564           epp       seg,si              " Make the SDW again
565           tsx2      bootload_dseg$make_sdw
566 
567           ldx3      segptr              " segno * 2 in X3
568           adlx3     segptr
569           staq      ds|0,x3             " Ka-ching!
570           lda       bootload_info$system_type
571           cmpa      ADP_SYSTEM,dl
572           tze       2,ic
573           cams      0
574           tra       0,x7                " and return
575 " ^L
576 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
577 "                                                                     "
578 "         read: subroutine to read the input.                         "
579 "                                                                     "
580 "         calling sequence:   tsx2      read                          "
581 "                             arg       loc                           "
582 "                             arg       =nwords                       "
583 "                                                                     "
584 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
585 
586           even
587           segdef    tape_status
588 tape_status:
589           bss       ,2
590 ndata:    bss       ,1
591 exprec:   dec       0
592 length:   dec       0
593 read_skip:dec       0
594 
595 read:     stc1      read_skip           " this is read
596           epp       seg,0,x2*           " get ptr to data loc
597           lxl7      1,x2*               get length
598           tra       read_join
599 
600 skip:     stz       read_skip           " this is skip
601           lxl7      0,x2*               " length
602 
603 read_join:
604           stx7      length
605           Bpush
606 
607 readloop:
608           ldx6      ndata               get data length of tape record
609           sblx6     prb|prb.ctl         compute # of words remaining in buffer
610           tze       readp               read next record if no more words left
611           cmpx6     length              get min (length, words_remaining)
612           tmoz      *+2                 ..
613           ldx6      length              ..
614 
615           szn       read_skip           " reading?
616           tze       no_read             " no
617 
618           ldq       prb|prb.ctl         tape buffer offset in QU
619           eaa       0,x6                words to copy in AU
620           lls       2                   generate character count and offset
621           mlr       (pr,rl,qu),(pr,rl)  copy data from tape buffer
622           desc9a    prb|prb.data+mstr_header_size,au
623           desc9a    seg|0,au
624 
625           epp       seg,seg|0,x6        bump pr6 by # of words copied
626 
627 no_read:
628           eaa       0,x6                words copied in AU
629           asa       prb|prb.ctl         bump record index
630           neg       0                   complement
631           asa       length              decrement length
632           tpnz      readloop            loop if more data to copy
633 
634 read_return:
635           szn       read_skip           " return to caller
636           tze       skip.return         " skip
637           Breturn   2
638 skip.return:
639           Breturn   1
640 " ^L
641 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
642 "                                                                     "
643 "         Subroutine to read one physical record from tape            "
644 "                                                                     "
645 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
646 
647 readp:    rscr      32                            " get time
648           staq      io_start_time                 " and save it
649           stz       prb|prb.ctl                   reset pointer on PRB to zero wds xmitted.
650           stz       prb|prb.data+mstr_header.c1   and smash the magic word
651 try_readp_again:
652           tsx2      bootload_io$connect           " Read a record
653           arg       bootload_info$tape_iom_number
654           arg       bootload_info$tape_channel_number
655           arg       tape_io_pcw
656           arg       read_tape_idcw
657 
658           staq      tape_status
659           cana      bootload_info$status_mask
660           tnz       tape_error
661           canq      =o7777,dl                     " tally residue?
662           tnz       readp                         " yes, skip it
663 
664           ldq       prb|prb.data+mstr_header.c1   " a quick spot check of the record.
665           cmpq      magic_first                   we know the first and last words.
666           tnz       readp                         it is probably just noise
667 
668 " See the longish comment earlier about the dependencies between data_bits_used
669 " and data_bit_len, and why the following code works despite its bad habits.
670 
671           ldq       prb|prb.data+mstr_header.data_bits_used " get bit count of data bits
672           div       36,dl               compute data word count
673           stq       ndata               and save
674 
675           eax2      0,ql                x2 is trailer pointer
676           ldq       prb|prb.data+mstr_header_size+mstr_trailer.c2,x2
677           cmpq      magic_last
678           tnz       readp               " bad record
679 
680           lda       prb|prb.data+mstr_header_size+mstr_trailer.tot_rec_word,x2 currec
681           cmpa      exprec              expected record?
682           tnz       bootload_error$bad_mst
683 
684           aos       exprec              save for next read
685           tra       readloop            " keep reading
686 
687 tape_error:
688           ana       =o770000,du         " get just major/minor status
689           cmpa      =o440000,du         " EOF?
690           tze       readp               " yes, skip it
691           cmpa      =o600000,du         " POF?
692           tze       retry_pof           " try again
693           tra       bootload_error$tape_error
694 
695 retry_pof:
696           szn       bootload_info$cold_tape_mpc   " F/W yet?
697           tze       chk_pof_time        " yes, leave it alone
698           szn       unwedged            " has unwedging been done
699           tnz       chk_pof_time        " yes, just chk time & retry I/O
700           tsx2      bootload_tape_fw$reset_and_mask " no, do the unwedging
701           stc1      unwedged            " show that it has been done
702 
703 chk_pof_time:
704           rscr      32                  " get the time
705           sbaq      io_start_time       " rel-a-tize
706           cmpaq     thirty_sec_limit    " is thirty seconds up?
707           tmi       try_readp_again     " NO, try one mo time
708           tra       bootload_error$tape_error " report error
709 
710 adjust_tape_device:
711 
712           szn       bootload_info$cold_tape_mpc   " F/W yet?
713           tnz       0,x2                          " no, leave it alone
714           lda       bootload_info$tape_device_number
715           als       24
716           stca      read_tape_idcw,20
717           lda       =o3000,dl
718           stca      read_tape_idcw,2
719           tra       0,x2                " return
720 " ^L
721 magic_first:
722           oct       670314355245        magic number at word 1 of record
723 magic_last:
724           oct       265221631704        magic number at end of record
725 
726 unwedged: bss       ,1
727           even
728 io_start_time:
729           bss       ,2
730 thirty_sec_limit:
731           dec       0,30000000          " thirty seconds (in micros)
732 
733           make_pcw  tape_io_pcw,        " PCW to reset status before tape I/O
734                     TAPE.reset_status,
735                     0,
736                     0,
737                     nondata,
738                     terminate,
739                     1
740 
741 read_tape_idcw:
742           vfd       6/TAPE.read_binary_record,6/0,6/0,3/7,1/0,2/0,6o/0,6/0
743 
744           make_ddcw read_tape_ddcw,     " DDCW describing physical_record_buffer
745                     0,
746                     mstr_header_size+1024+mstr_trailer_size,
747                     iotd
748 
749           end