ON ERROR ERROR EXT ERR, REPORT$+" at line "+STR$ ERL

SYS "XOS_ReadVarVal","Printers$AsmTrace",0,-1,0,3 TO,,trace%
REM Warning! The trace support code is pretty much a crock of ****.
REM It doesn't preserve any flags and just spits the data out to the
REM screen. C'est la vie.

REM These *MUST* be the same as the values in RunImageZZ
PrinterBufferSize = 1024
PrinterBufferSize2 = PrinterBufferSize/2
PrinterBufferSize4 = PrinterBufferSize/4

VARIND=&30
STOREA=&34
STSTORE=&38

LR=14

code_soft_hyphen=&19
code_stretch_space=&1C
code_indent_space=&1D
code_soft_space=&1E
code_back_char=&8
code_tab_char=&9
code_cond_page=&B
code_uncond_page=&C
code_line_char=&A
code_return_char=&D
code_esc_char=&1B
code_footnote_char=&18
code_format_char=&1F
code_ruler_char=ASC"9"
code_layout_char=ASC"0"
code_footstart_char=ASC"N"
code_footend_char=ASC"E"

psup=1

queu=2

tpub=3

magic=4:p%=0
 magic_psup=FNws(4)
 magic_prnt=FNws(4)
 magic_queu=FNws(4)
 magic_tpub=FNws(4)
 magic_task_buff=FNws(4)
 magic_reasn_string=FNws(4)
 magic_argp=FNws(4)
 magic_r14=FNws(4)

interface=12

line_space%=44

DIM code% 20000

FORi%=4 TO 6 STEP 2
P%=0:O%=code%
[ OPT i%
.entry_dot_matrix            B dot_matrix_entry       ; 0
.entry_postscript            B postscript_entry       ; 4
.entry_heap_initialise       B heap_initialise        ; 8
.entry_heap_claim            B heap_claim             ; 12
.entry_heap_free             B heap_free              ; 16
.entry_heap_extend           B heap_extend            ; 20
.entry_add_queue_entry       B add_queue_entry        ; 24
.entry_store_string          B store_string           ; 28
.entry_redraw_queue_below    B redraw_queue_below     ; 32
.entry_update_queue_window   B update_queue_window    ; 36
.entry_locate_entry          B locate_entry           ; 40
.entry_locate_entry_a        B locate_entry_a         ; 44
.entry_visualise_entry       B visualise_entry        ; 48
.entry_terminate_lines       B terminate_lines        ; 52
.entry_match_line            B match_line             ; 56 - not implemented
.entry_match_any_line        B match_any_line         ; 60 - not implemented
.entry_rtoz                  B rtoz                   ; 64
.entry_ctoz                  B ctoz                   ; 68

   FNddt_include

.heap_initialise
   STMFD   R13!, {interface,LR}
   MOV     interface, R0
   SWI     "XOS_ReadMemMapInfo"
   STR     R0, [interface, #CONST_interface_heap_psize%]
   SWI     "XOS_GetEnv"
   STR     R1, [interface, #CONST_interface_heap_start%]
   MOV     R0, #0
   STR     R0, [interface, #CONST_interface_heap_size%]
   BL      heap_increase
   LDMFD   R13!, {interface,PC}

.heap_freespace
; returns size of largest available block in R0
; since heap_freespace can ONLY be called privately, R12 is
; already pointing at our workspace.
   FNtrace("heap_freespace")
   LDR     R0, [interface, #CONST_interface_heap_size%]
   FNtrace("existing heap size:")
   FNtrace_reg(0)
   TEQ     R0, #0
   MOVEQ   PC, LR

   STMFD   R13!, {R1-R3,LR}
   MOV     R0, #1
   LDR     R1, [interface, #CONST_interface_heap_start%]
   FNtrace("heap start @")
   FNtrace_reg(1)
   SWI     "XOS_Heap"
   MOVVC   R0, R2
   FNtrace("heap_freespace done, size = ")
   FNtrace_reg(0)
   LDMFD   R13!, {R1-R3,PC}

.heap_increase
; since heap_increase can ONLY be called privately, R12 is
; already pointing at our workspace.
   STMFD   R13!, {R0-R3,LR}
   FNtrace("heap_increase")
   MVN     R0, #0
   MVN     R1, #0
   FNtrace("getting existing slot size ...")
   SWI     "XWimp_SlotSize"
   FNtrace_reg(0)
   LDR     R1, [interface, #CONST_interface_heap_psize%]
   FNtrace("got page size of")
   FNtrace_reg(1)
   ADD     R0, R0, R1
   MVN     R1, #0
   FNtrace("setting new slot size to")
   FNtrace_reg(0)
   SWI     "XWimp_SlotSize"
   ;;;BVS     xxx
   LDR     R1, [interface, #CONST_interface_heap_start%]
   LDR     R3, [interface, #CONST_interface_heap_psize%]
   LDR     R0, [interface, #CONST_interface_heap_size%]
   TEQ     R0, #0
   MOVNE   R0, #5
   FNtrace("calling OS_Heap to extend heap")
   FNtrace_reg(0)
   FNtrace_reg(1)
   FNtrace_reg(3)
   SWI     "XOS_Heap"
   BVS     yyy
   FNtrace("OS_Heap succeeded")
   LDR     R0, [interface, #CONST_interface_heap_size%]
   ADD     R0, R0, R3
   STR     R0, [interface, #CONST_interface_heap_size%]
   FNtrace("heap size is now ...")
   FNtrace_reg(0)
.yyy
.xxx
   STRVS   R0, [R13]
   LDMFD   R13!, {R0-R3,PC}

.heap_claim
; R0 = interface ptr
; R1 = ident value
; R2 = size required
;
; Returns with R0 pointing to the claimed block
   STMFD   R13!, {interface,LR}
   FNtrace("heap_claim")
   FNtrace_reg(0)
   FNtrace_reg(1)
   FNtrace_reg(2)
   MOV     interface, R0
   ADD     R3, R2, #4
.hc_1
   FNtrace("calling heap_freespace")
   BL      heap_freespace
   FNtrace("called heap_freespace")
   CMP     R3, R0
   BLT     hc_2
   FNtrace("calling heap_increase")
   BL      heap_increase
   FNtrace("called heap_increase")
   MOVVS   R0, #0
   LDMVSFD R13!, {interface,PC}
   B       hc_1
.hc_2
   STMFD   R13!, {R1}
   MOV     R0, #2
   LDR     R1, [interface, #CONST_interface_heap_start%]
   SWI     "XOS_Heap"
   LDMFD   R13!, {R1}
   STRVC   R1, [R2], #4
   MOVVC   R0, R2
   MOVVS   R0, #0
   FNtrace("heap_claim done")
   LDMFD   R13!, {interface,PC}

.heap_free
; R0 = interface ptr
; R1 = ident value
; R2 = block to free
   TEQ     R2, #0
   MOVEQ   PC, LR
   STMFD   R13!, {interface,LR}
   MOV     interface, R0
   SUB     R2, R2, #4
   LDR     R0, [R2]
   TEQ     R0, R1
   BNE     hf_1
   MOV     R0, #0
   STR     R0, [R2]
   MOV     R0, #3
   LDR     R1, [interface, #CONST_interface_heap_start%]
   SWI     "XOS_Heap"
   LDMFD   R13!, {interface,PC}
.hf_1
   ADR     R0, heap_free_error
   LDMFD   R13!, {interface,LR}
   TEQ     R0, R0
   TEQ     PC, PC
   ORRNES  PC, LR, #1<<28
   EQUD    %11100011001010001111001000000001 ; MSR     CPSR_f, #1<<28
   MOV     PC, LR

.heap_free_error
   EQUD    CONST_error_ok%
   EQUS    "CptBkNF"+CHR$0
   ALIGN

.heap_extend
; R0 = interface ptr
; R1 = ident value
; R2 = block ptr
; R3 = size
   STMFD   R13!, {interface,LR}
   MOV     interface, R0
   SUB     R2, R2, #4
   LDR     R0, [R2]
   TEQ     R0, R1
   BNE     he_1
   STMFD   R13!, {R3}
   MOV     R0, #6
   LDR     R1, [interface, #CONST_interface_heap_start%]
   SWI     "XOS_Heap"
   LDMFD   R13!, {R1}
   LDMVSFD R13!, {interface,PC}
   ADD     R4, R3, R1
.he_2
   BL      heap_freespace
   CMP     R4, R0
   BLT     he_3
   BL      heap_increase
   B       he_2
.he_3
   MOV     R0, #4
   MOV     R3, R1
   LDR     R1, [interface, #CONST_interface_heap_start%]
   SWI     "XOS_Heap"
   ADDVC   R0, R2, #4
   LDMFD   R13!, {interface,PC}
.he_1
   ADR     R0, heap_extend_error
   LDMFD   R13!, {interface,LR}
   TEQ     R0, R0
   TEQ     PC, PC
   ORRNES  PC, LR, #1<<28
   EQUD    %11100011001010001111001000000001 ; MSR     CPSR_f, #1<<28
   MOV     PC, LR

.heap_extend_error
   EQUD    CONST_error_ok%
   EQUS    "CptBkNX"+CHR$0
   ALIGN

.ident_QUEU
   EQUD    CONST_ident_QUEU%

.add_queue_entry
; R0 = interface ptr
; R1 = temp file flag
; R2 = ptr to sender string
; R3 = ptr to pathname string
; R4 = ptr to leafname string
; R5 = prnt
; R6 = file type
   STMFD   R13!, {interface,LR}
   MOV     interface, R0                                 ; copy the interface ptr
   STMFD   R13!, {R1-R6}                                 ; and stack the registers
   LDR     R1, ident_QUEU                                ; claim a queue block
   MOV     R2, #CONST_queu_MAX%
   BL      heap_claim
   TEQ     R0, #0
   ADREQ   R0, aqe_nomemory
   CMPEQ   PC, #&80000000                                ; set V
   LDMVSFD R13!, {interface,PC}
   MOV     R1, #0                                        ; initialise the queue block
   STR     R1, [R0, #CONST_queu_next%]
   STR     R1, [R0, #CONST_queu_handle_in%]
   STR     R1, [R0, #CONST_queu_handle_out%]
   STR     R1, [R0, #CONST_queu_perc%]
   STR     R1, [R0, #CONST_queu_tpub%]
   STR     R1, [R0, #CONST_queu_tprv%]
   STR     R1, [R0, #CONST_queu_tbff%]
   STR     R1, [R0, #CONST_queu_buff_in%]
   STR     R1, [R0, #CONST_queu_buff_out%]
   STR     R1, [R0, #CONST_queu_char%]
   STR     R1, [R0, #CONST_queu_scratch_name%]
   STR     R1, [R0, #CONST_queu_scratch_file%]
   STR     R1, [interface, #CONST_interface_nudged_prnt%]    ; and some globals
   STR     R1, [interface, #CONST_interface_nudged_queu%]
   STR     R1, [interface, #CONST_interface_queue_tempfile%]
   MOV     R1, #3
   STRB    R1, [R0, #CONST_queu_time%]
   STMFD   R13!, {R0}
   ADD     R1, R0, #CONST_queu_time%
   MOV     R0, #14
   SWI     "XOS_Word"
   LDMFD   R13!, {R11}
   LDMFD   R13!, {R1-R6}
   LDR     R0, [R5, #CONST_prnt_queu%]                       ; link the queue block into the
   TEQ     R0, #0                                            ; list. if the head is null,
   STREQ   R11, [R5, #CONST_prnt_queu%]                      ; put it at the head
   BEQ     aqe_1
.aqe_2
   LDR     R7, [R0, #CONST_queu_next%]                       ; otherwise find the end of the
   TEQ     R7, #0                                            ; list
   MOVNE   R0, R7
   BNE     aqe_2
   STR     R11, [R0, #CONST_queu_next%]
.aqe_1
   STMFD   R13!, {R5}                                        ; preserve the prnt value
   STR     R6, [R11, #CONST_queu_type%]                      ; store the file type
   TEQ     R1, #0                                            ; and store the "temp file" status
   MOVEQ   R7, #0
   MOVNE   R7, #2
   STRB    R7, [R11, #CONST_queu_status%]
   MOV     R0, interface                                     ; store the sender string
   MOV     R1, R2
   MOV     R2, #2
   BL      store_string
   BVS     aqe_error
   STR     R0, [R11, #CONST_queu_sender%]
   MOV     R0, interface                                     ; store the path string
   MOV     R1, R3
   MOV     R2, #2
   BL      store_string
   BVS     aqe_error
   STR     R0, [R11, #CONST_queu_path%]
   MOV     R0, interface                                     ; store the leafname string
   MOV     R1, R4
   MOV     R2, #2
   BL      store_string
   BVS     aqe_error
   STR     R0, [R11, #CONST_queu_leaf%]
   MOV     R0, #17                                           ; get the extent
   MOV     R1, R3
   SWI     "XOS_File"
   BVS     aqe_error
   TEQ     R0, #1
   MOVNE   R4, #0
   STR     R4, [R11, #CONST_queu_ext%]
   LDR     R0, [interface, #CONST_interface_global_mask%]
   BIC     R0, R0, #NOT(-2)
   STR     R0, [interface, #CONST_interface_global_mask%]
   LDR     R0, [interface, #CONST_interface_queue_entries%]
   ADD     R0, R0, #1
   STR     R0, [interface, #CONST_interface_queue_entries%]
   LDR     R1, [interface, #CONST_interface_scratch%]
   LDR     R0, [interface, #CONST_interface_queue%]
   STR     R0, [R1]
   ORR     R1, R1, #1
   SWI     "XWimp_GetWindowInfo"
   BVS     aqe_error
   LDMFD   R13!, {R5}
   BIC     R1, R1, #1
   LDR     R0, [R1, #32]
   ANDS    R0, R0, #(1<<16)
   LDMEQFD R13!, {interface,PC}
   LDR     R0, [interface, #CONST_interface_printer_active%]
   MOV     R2, #(line_space%*2)
   MUL     R0, R2, R0
   LDR     R3, [interface, #CONST_interface_queue_entries%]
   MOV     R2, #line_space%
   MUL     R3, R2, R3
   LDR     R4, [R1, #56]
   SUB     R4, R4, R0
   SUB     R4, R4, R3
   STR     R4, [R1, #48]
   LDR     R0, [interface, #CONST_interface_queue%]
   ADD     R1, R1, #44
   SWI     "XWimp_SetExtent"
   LDMVSFD R13!, {interface,PC}
   SUB     R1, R1, #44
   SWI     "XWimp_OpenWindow"
   LDMVSFD R13!, {interface,PC}
   MOV     R0, interface
   MOV     R1, R5
   MOV     R2, R11
   BL      redraw_queue_below
   LDMFD   R13!, {interface,PC}

.aqe_error
   LDMFD   R13!, {R5}
   LDMFD   R13!, {interface,PC}

.aqe_nomemory
   EQUD    CONST_error_fatal%
   EQUS    "NoMem"+CHR$0
   ALIGN

.store_string
; R0 = interface ptr
; R1 = pointer to string
; R2 = storage type
; R3 = length of string (if storage type = 5)
   STMFD   R13!, {R1-R5,interface,LR}
   MOV     interface, R0                 ; preserve interface ptr

   FNtrace("store_string")
   FNtrace_reg(2)

   TEQ     R2, #4                        ; have we got to GSTRANS it first?
   BNE     ss_2
   ; yes - do it into the scratch buffer
   MOV     R0, R1
   LDR     R1, [interface, #CONST_interface_scratch%]
   MOV     R2, #256
   SWI     "XOS_GSTrans"
   MOV     R3, R2                        ; keep the length
   LDR     R1, [interface, #CONST_interface_scratch%]    ; update R1 and R2
   MOV     R2, #5                        ; now got a GSTRANsed string
.ss_2
   TEQ     R2, #5                        ; string already GSTRANSed?
   BEQ     ss_2a                         ; if so, length is already in R3

   MOV     R0, R1                        ; got to find the length now
   MOV     R3, #0
.ss_1
   LDRB    R4, [R0], #1                  ; we'll accept termination by
   TEQ     R4, #0                        ; null
   TEQNE   R4, #13                       ; CR or
   TEQNE   R4, #10                       ; LF
   ADDNE   R3, R3, #1
   BNE     ss_1
.ss_2a
   TEQ     R3, #0                        ; if no string, return null pointer
   MOVEQ   R0, #0
   LDMEQFD R13!, {R1-R5,interface,PC}

   STMFD   R13!, {R1-R3}                 ; preserve ptr, type, length

   FNtrace("calling heap_claim")

   MOV     R0, interface                 ; claim a block of the right type
   LDR     R1, ident_GSTR
   TEQ     R2, #2
   LDREQ   R1, ident_STRG
   TEQ     R2, #3
   LDREQ   R1, ident_STR0
   ADD     R2, R3, #1                    ; length (allow for terminator)
   BL      heap_claim
   TEQ     R0, #0
   ADREQ   R0, aqe_nomemory
   CMPEQ   PC, #&80000000                                ; set V

   FNtrace("heap_claim done")

   LDMFD   R13!, {R1-R3}                 ; then restore regs

   LDMVSFD R13!, {R1-R5,interface,PC}

   MOV     R4, R0                        ; copy pointer to block into R4
   TEQ     R2, #5                        ; and if a GSTRANSed string,
   BNE     ss_3
   STRB    R3, [R4], #1                  ; store the length first
.ss_3
   LDRB    R5, [R1], #1                  ; then copy the string
   STRB    R5, [R4], #1
   SUBS    R3, R3, #1                    ; while we've still got some to do
   BNE     ss_3
   MOV     R5, #0                        ; then terminate as required
   TEQ     R2, #2
   MOVEQ   R5, #13
   TEQNE   R2, #3
   STREQB  R5, [R4]

   FNtrace("store_string done")

   LDMFD   R13!, {R1-R5,interface,PC}

.ident_GSTR
   EQUD    CONST_ident_GSTR%
.ident_STRG
   EQUD    CONST_ident_STRG%
.ident_STR0
   EQUD    CONST_ident_STR0%

.redraw_queue_below
; R0 = interface ptr
; R1 = prnt
; R2 = queu
   STMFD   R13!, {interface,LR}
   MOV     interface, R0
   LDR     R0, [interface, #CONST_interface_queue%]      ; get handle of queue window
   BL      window_open                                   ; is it open?
   LDMEQFD R13!, {interface,PC}                          ; if not, don't bother redrawing
   MOV     R0, interface                                 ; find out where to redraw
   BL      visualise_entry                               ; on exit, R0 = y1%
   LDR     R1, [interface, #CONST_interface_scratch%]    ; get the window information
   LDR     R2, [interface, #CONST_interface_queue%]
   STR     R2, [R1]
   ORR     R1, R1, #1
   SWI     "XWimp_GetWindowInfo"
   LDMVSFD R13!, {interface,PC}
   BIC     R1, R1, #1
   LDR     R2, [R1, #48]
   LDR     R3, [R1, #56]
   SUB     R2, R2, R3                                    ; R2 = !48-!56 (y2%)
   LDR     R4, [R1, #16]
   LDR     R5, [R1, #8]
   SUB     R4, R4, R5                                    ; R4 = !16-!8
   LDR     R5, [R1, #24]                                 ; R5 = !24     (yv1%)
   SUB     R4, R5, R4                                    ; R4 = R5 - R4 (yv2%)
   CMP     R2, R5                                        ; if y2%>yv1%
   LDMGTFD R13!, {interface,PC}                          ; return
   CMP     R0, R4                                        ; if y1%<yv2%
   LDMLTFD R13!, {interface,PC}                          ; return
   CMP     R5, R0                                        ; if yv1%<y1%
   MOVLT   R0, R5                                        ; y1%=yv1%
   CMP     R4, R2                                        ; if yv2%>y2%
   MOVGT   R2, R4                                        ; y2%=yv2%
   LDR     R3, [R1, #12]
   LDR     R6, [R1, #4]
   SUB     R3, R3, R6
   STR     R3, [R1, #12]                                 ; !12=!12-!4
   MOV     R3, #0
   STR     R3, [R1, #4]                                  ; !4=0
   STR     R2, [R1, #8]                                  ; !8=y2%
   STR     R0, [R1, #16]                                 ; !16=y1%
;   SWI     "XWimp_UpdateWindow"
;   MOV     R2, R0
;   MOV     R0, interface
;   BLVC    update_queue_window

   ; use ForceRedraw to get it to work with tiled backgrounds
   LDR     R0, [R1]
   LDR     R2, [R1, #8]
   LDR     R3, [R1, #12]
   LDR     R4, [R1, #16]
   LDR     R1, [R1, #4]
   SWI     "XWimp_ForceRedraw"

   LDMFD   R13!, {interface,PC}

.update_queue_window
; R0 = interface ptr
; R1 = buffer
; R2 = more flag
   STMFD   R13!, {interface,LR}
   MOV     interface, R0
   MOV     R0, R2
.uqw_1
   TEQ     R0, #0
   LDMEQFD R13!, {interface,PC}
   LDR     R3, [R1, #16]
   LDR     R4, [R1, #24]
   SUB     R3, R3, R4                                    ; R3 (y%) = !16 - !24
   LDR     R4, [R1, #40]
   SUB     R4, R3, R4                                    ; R4 (y1%) = y% - !40
   LDR     R5, [R1, #32]
   SUB     R5, R3, R5                                    ; R5 (y2%) = y% - !32
   CMP     R4, #0
   MOVLT   R4, #0
   CMP     R5, R4
   BLT     uqw_2
   MOV     R2, #0
   SUB     R3, R2, R4                                    ; y% = -y1%
   SUB     R5, R2, R5                                    ; y2% = -y2%
   STMFD   R13!, {R1}
   MOV     R0, interface
   MOV     R1, R3
   BL      locate_entry
   MOV     R3, R0
.uqw_3
   BL      produce_entry
   CMP     R3, R5
   BGE     uqw_3                                         ; repeat until y% < y2%
   LDMFD   R13!, {R1}
.uqw_2
   LDR     R0, [interface, #CONST_interface_queue%]      ; get handle of queue window
   STR     R0, [R1]                                      ; 'cos the scratch buffer gets destroyed
   SWI     "XWimp_GetRectangle"
   LDMVSFD R13!, {interface,PC}
   B       uqw_1

.locate_entry_a
; As for locate_entry, except that R1 and R2 are
; stored in the scratch buffer
   STMFD   R13!, {interface,LR}
   STMFD   R13!, {R0}                                    ; need to preserve R0
   BL      locate_entry
   LDMFD   R13!, {interface}                             ; so that it can be used!
   LDR     R3, [interface, #CONST_interface_scratch%]
   STR     R1, [R3]
   STR     R2, [R3, #4]
   LDMFD   R13!, {interface,PC}

.locate_entry
; R0 = interface ptr
; R1 = specified y offset
; Returns R0 = y, R1 = prnt, R2 = queu
   STMFD   R13!, {R3-R5,interface,LR}
   MOV     interface, R0
   MOV     R0, R1
   LDR     R1, [interface, #CONST_interface_printer_installed%]
   MOV     R3, R1                                        ; last_prnt = prnt
   MOV     R2, #0                                        ; queu = 0
   MOV     R4, #0                                        ; this_y = 0
.le_1
   TEQ     R1, #0                                        ; while prnt
   BEQ     le_2
   LDR     R5, [R1, #CONST_prnt_flags%]
   ANDS    R5, R5, #1                                    ; if prnt!prnt_flags AND 1
   BEQ     le_3
   SUB     R5, R4, #line_space%
   CMP     R0, R5                                        ; if y>this_y - line_space
   BLE     le_4
   CMP     R0, R4                                        ; and y<=this_y
   BGT     le_4
   MOV     R0, R4                                        ; y = this_y
   B       le_9                                          ; hit on the header
.le_4
   SUB     R4, R4, #line_space%                          ; this_y -= line_space
   LDR     R2, [R1, #CONST_prnt_queu%]                   ; queu = prnt!prnt_queu
.le_5
   TEQ     R2, #0                                        ; while queu
   BEQ     le_6
   SUB     R5, R4, #line_space%
   CMP     R0, R5                                        ; if y>this_y - line_space
   BLE     le_7
   CMP     R0, R4                                        ; and y<=this_y
   BGT     le_7
   MOV     R0, R4                                        ; y = this_y
   B       le_9                                          ; hit on this queue entry
.le_7
   SUB     R4, R4, #line_space%
   LDR     R2, [R2, #CONST_queu_next%]
   B       le_5
.le_6
   SUB     R5, R4, #line_space%
   CMP     R0, R5                                        ; if y>this_y - line_space
   BLE     le_8
   CMP     R0, R4                                        ; and y<=this_y
   BGT     le_8
   MVN     R2, #0                                        ; queu = -1
   MOV     R0, R4                                        ; y = this_y
   B       le_9                                          ; hit on the footer
.le_8
   SUB     R4, R4, #line_space%
   MOV     R3, R1                                        ; last_prnt = prnt
.le_3
   LDR     R1, [R1, #CONST_prnt_next%]
   B       le_1
.le_2
   MOV     R1, R3
   MVN     R2, #0
   MOV     R0, R4
.le_9
   LDMFD   R13!, {R3-R5,interface,PC}

.tag_qt1
   EQUS    "QT1"+CHR$0
.tag_qt2
   EQUS    "QT2"+CHR$0
.tag_qf1
   EQUS    "QF1"+CHR$0
.tag_qf2
   EQUS    "QF2"+CHR$0
.tag_qf3
   EQUS    "QF3"+CHR$0
.tag_qf4
   EQUS    "QF4"+CHR$0
.tag_qu1
   EQUS    "QU1"+CHR$0
.tag_qu3
   EQUS    "QU3"+CHR$0

.produce_entry
; since produce_entry can ONLY be called privately, R12 is
; already pointing at our workspace.
; R1 = prnt
; R2 = queu
; R3 = y
   STMFD   R13!, {R5,LR}
   CMP     R1, #0
   BEQ     pe_1
   CMN     R2, #1                                        ; R2 = -1?
   BEQ     pe_2
   CMP     R2, #0
   BEQ     pe_3
   ; otherwise output a queue entry
   LDRB    R4, [R2, #CONST_queu_status%]
   ANDS    R4, R4, #8
   LDR     R5, [interface, #CONST_interface_queue_1%]
   LDR     R6, [R5, #16]
   BICEQ   R6, R6, #1<<21
   ORRNE   R6, R6, #1<<21
   STR     R6, [R5, #16]
   LDR     R5, [interface, #CONST_interface_queue_2%]
   LDR     R6, [R5, #16]
   BICEQ   R6, R6, #1<<21
   ORRNE   R6, R6, #1<<21
   STR     R6, [R5, #16]
   LDR     R5, [interface, #CONST_interface_queue_3%]
   LDR     R6, [R5, #16]
   BICEQ   R6, R6, #1<<21
   ORRNE   R6, R6, #1<<21
   STR     R6, [R5, #16]
   LDR     R5, [interface, #CONST_interface_queue_4%]
   LDR     R6, [R5, #16]
   BICEQ   R6, R6, #1<<21
   ORRNE   R6, R6, #1<<21
   STR     R6, [R5, #16]
   MOV     R11, R3
   MOV     R10, R2
   MOV     R9, R1
   SUB     R13, R13, #256
   ; plot the time
   ADR     R1, tag_qf1                                   ; get the format string
   ADD     R2, R13, #64
   BL      pe_5
   ADD     R0, R10, #CONST_queu_time%                    ; then convert the time
   MOV     R1, R13
   MOV     R2, #64
   ADD     R3, R13, #64
   SWI     "XOS_ConvertDateAndTime"
   LDR     R1, [interface, #CONST_interface_queue_1%]    ; then plot the icon
   STR     R11, [R1, #12]
   SUB     R4, R11, #line_space%
   STR     R4, [R1, #4]
   STR     R13, [R1, #20]
   SWI     "XWimp_PlotIcon"
   ; plot the date
   ADR     R1, tag_qf2                                   ; get the format string
   ADD     R2, R13, #64
   BL      pe_5
   ADD     R0, R10, #CONST_queu_time%                    ; then convert the time
   MOV     R1, R13
   MOV     R2, #64
   ADD     R3, R13, #64
   SWI     "XOS_ConvertDateAndTime"
   LDR     R1, [interface, #CONST_interface_queue_2%]    ; then plot the icon
   STR     R11, [R1, #12]
   SUB     R4, R11, #line_space%
   STR     R4, [R1, #4]
   STR     R13, [R1, #20]
   SWI     "XWimp_PlotIcon"
   ; plot the size
   LDRB    R4, [R10, #CONST_queu_status%]
   AND     R4, R4, #5
   CMP     R4, #1
   CMPNE   R4, #4
   BEQ     pe_5a
   ; not paused or suspended so do the size
   LDR     R4, [R10, #CONST_queu_type%]
   CMP     R4, #&1000
   CMPNE   R4, #&2000
   BEQ     pe_5c
   ; not a directory or application, so REALLY do the size
   LDR     R0, [R10, #CONST_queu_ext%]
   MOV     R1, R13
   MOV     R2, #16
   SWI     "XOS_ConvertFileSize"                         ; filesize at [R13]
   LDR     R0, [R10, #CONST_queu_perc%]
   ADD     R1, R13, #32
   MOV     R2, #16
   SWI     "XOS_ConvertCardinal1"                        ; percentage at [R13+32]
   LDR     R0, [interface, #CONST_interface_host_desc%]
   LDR     R4, [R10, #CONST_queu_handle_in%]
   CMP     R4, #0                                        ; if file is open include the percentage
   ADRNE   R1, tag_qf3
   ADDNE   R4, R13, #32
   MOVNE   R5, R13
   ADREQ   R1, tag_qf4
   MOVEQ   R4, R13
   MOVEQ   R5, #0
   MOV     R6, #0
   MOV     R7, #0
   LDR     R2, [interface, #CONST_interface_scratch%]
   MOV     R3, #256
   SWI     "XMessageTrans_Lookup"
   B       pe_5z
.pe_5a     ; paused or suspended
   LDR     R2, [interface, #CONST_interface_scratch%]
   MOV     R0, #ASC"["
   STRB    R0, [R2], #1
   LDR     R0, [interface, #CONST_interface_host_desc%]
   CMP     R4, #1
   ADREQ   R1, tag_qu1
   ADRNE   R1, tag_qu3
   MOV     R3, #250
   MOV     R4, #0
   MOV     R5, #0
   MOV     R6, #0
   MOV     R7, #0
   SWI     "XMessageTrans_Lookup"
   ADD     R2, R2, R3
   MOV     R0, #ASC"]"
   STRB    R0, [R2], #1
   MOV     R0, #0
   STRB    R0, [R2]
   B       pe_5z
.pe_5c     ; directory or application
   LDR     R0, [interface, #CONST_interface_host_desc%]
   CMP     R4, #&1000
   ADREQ   R1, tag_qt1
   ADRNE   R1, tag_qt2
   LDR     R2, [interface, #CONST_interface_scratch%]
   MOV     R3, #256
   MOV     R4, #0
   MOV     R5, #0
   MOV     R6, #0
   MOV     R7, #0
   SWI     "XMessageTrans_Lookup"
.pe_5z
   LDR     R1, [interface, #CONST_interface_queue_3%]
   STR     R11, [R1, #12]
   SUB     R4, R11, #line_space%
   STR     R4, [R1, #4]
   LDR     R4, [interface, #CONST_interface_scratch%]
   STR     R4, [R1, #20]
   SWI     "XWimp_PlotIcon"
   ; plot the name
   MOV     R1, R13
   MOV     R0, #32
   STRB    R0, [R1], #1
   LDR     R2, [R10, #CONST_queu_sender%]
.pe_5y
   LDRB    R0, [R2], #1
   TEQ     R0, #13
   STRNEB  R0, [R1], #1
   BNE     pe_5y
   MOV     R2, #32
   STRB    R2, [R1], #1
   MOV     R0, #ASC"-"
   STRB    R0, [R1], #1
   STRB    R2, [R1], #1
   LDR     R2, [R10, #CONST_queu_leaf%]
   TEQ     R2, #0                                        ; could be no leaf!
   BEQ     pe_5w
.pe_5x
   LDRB    R0, [R2], #1
   TEQ     R0, #13
   STRNEB  R0, [R1], #1
   BNE     pe_5x
.pe_5w
   MOV     R0, #0
   STRB    R0, [R1], #1
   LDR     R1, [interface, #CONST_interface_queue_4%]    ; then plot the icon
   STR     R11, [R1, #12]
   SUB     R4, R11, #line_space%
   STR     R4, [R1, #4]
   STR     R13, [R1, #20]
   SWI     "XWimp_PlotIcon"
   ; restore the regs and leave
   ADD     R13, R13, #256
   MOV     R1, R9
   MOV     R2, R10
   MOV     R3, R11
   LDR     R2, [R2, #CONST_queu_next%]
   CMP     R2, #0
   MVNEQ   R2, #0
   B       pe_4

.pe_2                                                    ; post-printer queue blank line
;   STMFD   R13!, {R1}
;   LDR     R1, [interface, #CONST_interface_queue_5%]
;   STR     R3, [R1, #12]
;   SUB     R4, R3, #line_space%
;   STR     R4, [R1, #4]
;   SWI     "XWimp_PlotIcon"
;   LDMFD   R13!, {R1}
   MOV     R2, #0
.pe_2a
   LDR     R1, [R1, #CONST_prnt_next%]                   ; move on to the next printer
   TEQ     R1, #0                                        ; if we've run out of printers
   BEQ     pe_4                                          ; then get out now
   LDR     R4, [R1, #CONST_prnt_flags%]                  ; otherwise if the printer
   ANDS    R4, R4, #1                                    ; is not active
   BEQ     pe_2a                                         ; go for the next printer
   B       pe_4

.pe_3                                                    ; pre-printer queue header line
   STMFD   R13!, {R1-R3}
   SUB     R13, R13, #164                                ; should be enough!
   MOV     R11, R1                                       ; preserve prnt into R11
   MOV     R0, #ASC"C"                                   ; get the connection string
   STRB    R0, [R13]
   MOV     R0, #ASC"N"
   STRB    R0, [R13, #1]
   LDR     R0, [R11, #CONST_prnt_cnct%]
   LDR     R0, [R0, #CONST_cnct_type%]
   CMP     R0, #10                                       ; Change here from RJW to cope with
   BGE     twodigits_1                                   ; connection types > 10.
   ADD     R0, R0, #48
   STRB    R0, [R13, #2]
   MOV     R0, #0
   STRB    R0, [R13, #3]
   B       twodigits_2
.twodigits_1
   MOV     R1, #48
.twodigits_3
   SUB     R0, R0, #10
   ADD     R1, R1, #1
   CMP     R0, #10
   BGE     twodigits_3
   ADD     R0, R0, #48
   STRB    R1, [R13, #2]
   STRB    R0, [R13, #3]
   MOV     R0, #0
   STRB    R0, [R13, #4]
.twodigits_2
   MOV     R1, R13                                       ; point to the tag
   ADD     R2, R13, #12                                  ; point to some space
   BL      pe_5                                          ; and go get it

   MOV     R0, #ASC"Q"                                   ; now get the printer status string
   STRB    R0, [R13]
   MOV     R0, #ASC"U"
   STRB    R0, [R13,#1]
   LDR     R0, [R11, #CONST_prnt_flags%]
   AND     R0, R0, #%101100
   MOV     R4, #ASC"0"
   TEQ     R0, #4
   TEQNE   R0, #12
   MOVEQ   R4, #ASC"1"
   TEQ     R0, #32
   TEQNE   R0, #40
   MOVEQ   R4, #ASC"3"
   TEQ     R0, #8
   MOVEQ   R4, #ASC"2"
   STRB    R4, [R13, #2]
   MOV     R1, #0
   STRB    R1, [R13, #3]
   MOV     R1, R13
   ADD     R2, R13, #80                                  ; some stack space to use
   BL      pe_5                                          ; go get the string

   MOV     R0, #ASC"Q"                                   ; now build the whole thing
   STRB    R0, [R13]
   MOV     R0, #ASC"U"
   STRB    R0, [R13, #1]
   LDR     R4, [R11, #CONST_prnt_name%]                  ; where the printer name is stored
   TEQ     R4, #0
   MOVNE   R0, #ASC"A"
   MOVEQ   R0, #ASC"L"
   STRB    R0, [R13, #2]
   MOV     R0, #0
   STRB    R0, [R13, #3]
   ADD     R6, R13, #12                                  ; where we put the con string
   ADD     R7, R13, #80                                  ; where we put the status string
   LDR     R5, [R11, #CONST_prnt_type%]                  ; where the printer type is stored

   LDR     R0, [interface, #CONST_interface_host_desc%]
   MOV     R1, R13                                       ; pointer to the tag
   LDR     R2, [interface, #CONST_interface_scratch%]    ; where we want the string built
   MOV     R3, #256                                      ; how big the buffer is
   SWI     "XMessageTrans_Lookup"                        ; so go build it!

   ADD     R13, R13, #164                                ; restore the stack
   LDMFD   R13, {R1-R3}                                  ; and get our values back
   LDR     R1, [interface, #CONST_interface_queue_0%]    ; plotting icon 0
   STR     R3, [R1, #12]                                 ; store y
   SUB     R4, R3, #line_space%                          ; store y-line_space
   STR     R4, [R1, #4]
   LDR     R4, [interface, #CONST_interface_scratch%]    ; store buffer
   STR     R4, [R1, #20]
   LDR     R3, [R1, #16]
   BIC     R3, R3, #1<<21
   LDR     R2, [R11, #CONST_prnt_flags%]
   ANDS    R2, R2, #16
   ORRNE   R3, R3, #1<<21
   STR     R3, [R1, #16]
   SWI     "XWimp_PlotIcon"
   LDMFD   R13!, {R1-R3}
   LDR     R2, [R1, #CONST_prnt_queu%]
   TEQ     R2, #0
   MVNEQ   R2, #0
   B       pe_4

.pe_1
;   STMFD   R13!, {R1}
;   LDR     R1, [interface, #CONST_interface_queue_5%]
;   STR     R3, [R1, #12]
;   SUB     R4, R3, #line_space%
;   STR     R4, [R1, #4]
;   SWI     "XWimp_PlotIcon"
;   LDMFD   R13!, {R1}

.pe_4
   SUB     R3, R3, #line_space%
   LDMFD   R13!, {R5,PC}

.pe_5
; bastardised way of getting a message.
; R1 enters pointing at tag
; R2 enters pointing at buffer
   STMFD   R13!, {R1-R7,LR}
   LDR     R0, [interface, #CONST_interface_host_desc%]
   MOV     R3, #64
   MOV     R4, #0
   MOV     R5, #0
   MOV     R6, #0
   MOV     R7, #0
   SWI     "XMessageTrans_Lookup"
   LDMFD   R13!, {R1-R7,PC}

.window_open
; since window_open can ONLY be called privately, R12 is
; already pointing at our workspace.
; R0 = handle
; Returns Z set if window is closed
   STMFD   R13!, {R1,LR}
   LDR     R1, [interface, #CONST_interface_scratch%]
   STR     R0, [R1]
   SWI     "XWimp_GetWindowState"
   LDR     R0, [R1, #32]
   ANDS    R0, R0, #1<<16
   LDMFD   R13!, {R1,PC}

.visualise_entry
; R0 = interface ptr
; R1 = prnt
; R2 = queu
; Returns R0 = y
   STMFD   R13!, {R3-R4,interface,LR}
   MOV     interface, R0
   MOV     R0, #0                                        ; y = 0
   CMN     R2, #1                                        ; if queu = -1,
   MOVEQ   R2, #0                                        ; queu = 0
   LDR     R3, [interface, #CONST_interface_printer_installed%]
.ve_1
   TEQ     R3, #0                                        ; if this_prnt = 0
   MOVEQ   R0, #0                                        ; y = 0
   LDMEQFD R13!, {R3-R4,interface,PC}
   LDR     R4, [R3, #CONST_prnt_flags%]                  ; if prnt!prnt_flags AND 1
   ANDS    R4, R4, #1
   BEQ     ve_2
   CMP     R3, R1                                        ; if prnt=this_prnt and queu=0
   CMPEQ   R2, #0
   LDMEQFD R13!, {R3-R4,interface,PC}                    ; then hit on header
   SUB     R0, R0, #line_space%
   LDR     R4, [R3, #CONST_prnt_queu%]                   ; otherwise check the queue entries
.ve_3
   TEQ     R4, #0                                        ; while this_queu
   BEQ     ve_4
   CMP     R3, R1                                        ; if prnt=this_prnt
   CMPEQ   R4, R2                                        ; and queu=this_queu
   LDMEQFD R13!, {R3-R4,interface,PC}                    ; then hit on queue entry
   SUB     R0, R0, #line_space%
   LDR     R4, [R4, #CONST_queu_next%]
   B       ve_3
.ve_4
   SUB     R0, R0, #line_space%                          ; footer space
.ve_2
   LDR     R3, [R3, #CONST_prnt_next%]
   B       ve_1

.postscript_entry
; logic is:
;  get a byte
;  process it
;  loop until line flag>0 or split flag>0 or page flag>1 or EOF
   STMFD   R13!, {LR}
   STR     R8, [magic, #magic_argp]
   STR     LR, [magic, #magic_r14]
.postscript_loop
   ; *** BUG FIX! ***
   LDR     R0, [queu, #CONST_queu_buff_in%]             ; ensure that we don't overwrite
   CMP     R0, #PrinterBufferSize4                      ; the end of the buffer
   MOVGE   R0, #1                                       ; SMC - bug fix the bug fix - set the split flag
   STRGE   R0, [tpub, #CONST_tpub_split_flag%]
   LDMGEFD R13!, {PC}

   MOV     LR, R1                                       ; preserve R1 (honest!)
   LDR     R1, [queu, #CONST_queu_handle_in%]
   SWI     "XOS_BGet"                                   ; LR does NOT get stomped on
   LDMVSFD R13!, {PC}                                   ;  (exit if there was an error)
   MOV     R1, LR                                       ; 'cos the SWI is called in USR mode
   BL      text_trans
   LDR     R0, [tpub, #CONST_tpub_line_flag%]
   CMP     R0, #0
   LDMGTFD R13!, {PC}
   LDR     R0, [tpub, #CONST_tpub_split_flag%]
   CMP     R0, #0
   LDMGTFD R13!, {PC}
   LDR     R0, [tpub, #CONST_tpub_page_flag%]
   CMP     R0, #1
   LDMGTFD R13!, {PC}
   STMFD   R13!, {R1,R2}
   MOV     R0, #5
   LDR     R1, [queu, #CONST_queu_handle_in%]
   SWI     "XOS_Args"                                    ; test for EOF
   TEQ     R2, #0
   LDMFD   R13!, {R1,R2}
   BEQ     postscript_loop
   LDMFD   R13!, {PC}

.dot_matrix_entry
; logic is:
;  while we've still got some bytes to do
;    and page is not paused
;    and not EOF
;  get a byte
;  process it
;  loop
   STMFD   R13!, {LR}
   STR     R8, [magic, #magic_argp]
   STR     LR, [magic, #magic_r14]
   STMFD   R13!, {R5}                                    ; number of bytes to be output
.dot_matrix_loop
   LDR     R8, [tpub, #CONST_tpub_pause_flag%]
   TEQ     R8, #0
   LDMNEFD R13!, {R0,PC}                                 ; printer paused
   LDR     R8, [queu, #CONST_queu_buff_in%]
   LDMFD   R13, {R7}                                     ; get the value but leave it there
   CMP     R8, R7
   LDMGEFD R13!, {R0,PC}                                 ; done enough bytes
   STMFD   R13!, {R1,R2}
   MOV     R0, #5
   LDR     R1, [queu, #CONST_queu_handle_in%]
   SWI     "XOS_Args"                                    ; test for EOF
   TEQ     R2, #0
   LDMFD   R13!, {R1,R2}
   LDMNEFD R13!, {R0,PC}                                 ; got EOF
   MOV     LR, R1                                        ; preserve R1 (honest!)
   LDR     R1, [queu, #CONST_queu_handle_in%]
   SWI     "XOS_BGet"                                    ; LR does NOT get stomped on
   LDMVSFD R13!, {R5,PC}                                 ; exit if there is an error
   MOV     R1, LR
   BL      text_trans
   B       dot_matrix_loop

.text_trans
; on entry, R0 holds the character to process
; check for a pending escape first ...
   FNdebug("text_trans                ")
   LDR     R8, [tpub, #CONST_tpub_split_flag%]
   TEQ     R8, #0
   BNE     fn_split_flag

   LDR     R8, [tpub, #CONST_tpub_escape_flag%]
   TEQ     R8, #1
   BEQ     fn_escape_1
   TEQ     R8, #2
   BEQ     fn_escape_2
   TEQ     R8, #3
   BEQ     fn_escape_3
   TEQ     R8, #4
   BEQ     fn_escape_4

   LDR     R8, [tpub, #CONST_tpub_page_flag%]
   TEQ     R8, #0                                        ; NB. R8 is used by fn_page_flag
   BNE     fn_page_flag                                  ; as holding the flag value

   LDR     R8, [tpub, #CONST_tpub_format_flag%]
   TEQ     R8, #0
   BNE     fn_format_flag

   LDR     R8, [tpub, #CONST_tpub_ruler_flag%]
   TEQ     R8, #0
   BNE     fn_ruler_flag

   LDR     R8, [tpub, #CONST_tpub_layout_flag%]
   TEQ     R8, #0
   BNE     fn_layout_flag

   LDR     R8, [tpub, #CONST_tpub_line_flag%]
   TEQ     R8, #0                                        ; NB. R8 is used by fn_line_flag
   BNE     fn_line_flag                                  ; as holding the flag value

   LDR     R8, [tpub, #CONST_tpub_footnote_flag%]
   TEQ     R8, #0                                        ; NB. R8 is used by fn_footnote_flag
   BNE     fn_footnote_flag                              ; as holding the flag value

   LDR     R8, [tpub, #CONST_tpub_space_flag%]
   TEQ     R8, #0
   BNE     fn_space_flag

   LDR     R7, [psup, #CONST_psup_text%]
   AND     R7, R7, #%10                                  ; split flag needed?
   LDR     R8, [queu, #CONST_queu_buff_in%]
   CMP     R8, #PrinterBufferSize4                       ; have we used too much?
   MOVLE   R7, #0                                        ; no - clear the flag
   TEQ     R7, #0
   MOVNE   R8, #1                                        ; set the split flag
   STRNE   R8, [tpub, #CONST_tpub_split_flag%]
   BNE     unbget                                        ; and move back a char

   CMP     R0, #32
   BLT     text_trans_01
   CMP     R0, #127
   BGE     text_trans_01
   LDR     R7, [psup, #CONST_psup_text%]
   AND     R7, R7, #%1000                                ; multi-column?
   LDR     R8, [tpub, #CONST_tpub_cpos%]
   LDR     R6, [tpub, #CONST_tpub_xc%]
   CMP     R8, R6                                        ; have we gone too far?
   MOVLT   R7, #0                                        ; no - clear the flag
   TEQ     R7, #0
   MOVNE   R8, #1                                        ; set the line flag
   STRNE   R8, [tpub, #CONST_tpub_line_flag%]
   BNE     unbget                                        ; and move back a char
   ; otherwise
   ADD     R8, R8, #1                                    ; move forward one
   STR     R8, [tpub, #CONST_tpub_cpos%]
   B       translate_character

.text_trans_01
   FNdebug("text_trans_01             ")
   LDR     R7, [tpub, #CONST_tpub_cc_print%]
   TEQ     R7, #0
   BEQ     text_trans_02                                 ; standard cc handling
   CMP     R0, #127
   BGE     text_trans_01a                                ; top-bit-set char
   CMP     R0, #32
   BGE     text_trans_02                                 ; displayable char
   TEQ     R0, #code_return_char
   TEQNE   R0, #code_line_char
   BEQ     text_trans_02                                 ; normal handling for CR/LF
                                                         ; otherwise process control code

.text_trans_01a
   FNdebug("text_trans_01a            ")
   TEQ     R7, #1                                        ; display control codes?
   MOVNE   PC, LR                                        ; no - must ignore them
   LDR     R7, [queu, #CONST_queu_buff_in%]              ; remember where we are ...
   STMFD   R13!, {R7}
   MVN     R8, #5                                        ; and translate this control code
   STMFD   R13!, {LR}                                    ; (code -6)
   BL      text_printer_char                             ; into a printable character
   LDMFD   R13!, {LR}
   LDR     R7, [psup, #CONST_psup_text%]
   AND     R7, R7, #%1000                                ; multi-column?
   LDR     R8, [tpub, #CONST_tpub_cpos%]
   LDR     R6, [tpub, #CONST_tpub_xc%]
   CMP     R8, R6                                        ; gone too far?
   MOVLT   R7, #0                                        ; no - clear the flag
   TEQ     R7, #0
   STRNE   R6, [tpub, #CONST_tpub_cpos%]                 ; move to the edge of the column
   LDMNEFD R13!, {R6}                                    ; pull off the old buffer ptr
   STRNE   R6, [queu, #CONST_queu_buff_in%]              ; and restore it
   MOVNE   R8, #1
   STRNE   R8, [tpub, #CONST_tpub_line_flag%]
   BNE     unbget                                        ; and move back a char
   ; otherwise
   LDMFD   R13!, {R6}                                    ; pull off the old buffer ptr
   MOV     PC, LR                                        ; and return

.text_trans_02
   FNdebug("text_trans_02             ")
   TEQ     R0, #code_stretch_space
   TEQNE   R0, #code_indent_space
   TEQNE   R0, #code_soft_space
   MOVEQ   R8, #1
   STREQ   R8, [tpub, #CONST_tpub_space_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_line_char
   TEQNE   R0, #code_return_char
   MOVEQ   R8, #0
   STREQ   R8, [tpub, #CONST_tpub_cpos%]
   MOVEQ   R8, #1
   STREQ   R8, [tpub, #CONST_tpub_line_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_tab_char
   BNE     text_trans_02a
   LDR     R7, [psup, #CONST_psup_text%]
   AND     R7, R7, #%1000                                ; multi-column?
   LDR     R8, [tpub, #CONST_tpub_cpos%]
   ADD     R8, R8, #8                                    ; (cpos+8)
   MOV     R8, R8, LSR #3                                ; (cpos+8)DIV8
   MOV     R8, R8, LSL #3                                ; ((cpos+8)DIV8)*8
   LDR     R6, [tpub, #CONST_tpub_xc%]
   CMP     R8, R6                                        ; gone too far?
   MOVLT   R7, #0                                        ; no - clear the flag
   TEQ     R7, #0
   STRNE   R6, [tpub, #CONST_tpub_cpos%]                 ; move to the edge of the column
   MOVNE   R8, #1
   STRNE   R8, [tpub, #CONST_tpub_line_flag%]
   BNE     unbget
   ; otherwise
   MVN     R8, #15                                       ; code -16
   B       text_printer_string

.text_trans_02a
   FNdebug("text_trans_02a            ")
   TEQ     R0, #code_soft_hyphen
   BNE     text_trans_02b
   LDR     R7, [psup, #CONST_psup_text%]
   AND     R7, R7, #%1000                                ; multi-column?
   LDR     R8, [tpub, #CONST_tpub_cpos%]
   LDR     R6, [tpub, #CONST_tpub_xc%]
   CMP     R8, R6                                        ; gone too far?
   MOVLT   R7, #0                                        ; no - clear the flag
   TEQ     R7, #0
   MOVNE   R8, #1
   STRNE   R8, [tpub, #CONST_tpub_line_flag%]
   BNE     unbget
   ; otherwise
   MOV     R0, #ASC"-"
   B       text_trans

.text_trans_02b
   FNdebug("text_trans_02b            ")
   TEQ     R0, #code_back_char
   BNE     text_trans_02c
   LDR     R8, [tpub, #CONST_tpub_cpos%]
   CMP     R8, #0
   SUBNE   R8, R8, #1                                    ; show we've moved back
   STRNE   R8, [tpub, #CONST_tpub_cpos%]
   MVNNE   R8, #6                                        ; and actually do it (code -7)
   BNE     text_printer_string
   MOV     PC, LR                                       ; otherwise just return

.text_trans_02c
   FNdebug("text_trans_02c            ")
   TEQ     R0, #code_cond_page
   MOVEQ   R8, #1
   STREQ   R8, [tpub, #CONST_tpub_page_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_uncond_page
   MOVEQ   R8, #2
   STREQ   R8, [tpub, #CONST_tpub_page_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_esc_char
   MOVEQ   R8, #1
   STREQ   R8, [tpub, #CONST_tpub_escape_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_footnote_char
   MOVEQ   R8, 1
   STREQ   R8, [tpub, #CONST_tpub_footnote_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_format_char
   MOVEQ   R8, #1
   STREQ   R8, [tpub, #CONST_tpub_format_flag%]
   MOVEQ   PC, LR

   CMP     R0, #127
   MOVEQ   PC, LR

   CMP     R0, #32
   MOVLT   PC, LR
   LDR     R7, [psup, #CONST_psup_text%]
   AND     R7, R7, #%1000                                ; multi-column?
   LDR     R8, [tpub, #CONST_tpub_cpos%]
   LDR     R6, [tpub, #CONST_tpub_xc%]
   CMP     R8, R6                                        ; gone too far?
   MOVLT   R7, #0                                        ; no - clear the flag
   TEQ     R7, #0
   MOVNE   R8, #1
   STRNE   R8, [tpub, #CONST_tpub_line_flag%]
   BNE     unbget
   ; otherwise
   ADD     R8, R8, #1
   STR     R8, [tpub, #CONST_tpub_cpos%]
   B       translate_character                           ; yes, I know - it is just below

.translate_character
; look up the character in the list. If it is there, use the translation
; otherwise just output the character
   LDR     R8, [queu, #CONST_queu_char%]

.tc_00
   TEQ     R8, #0                                        ; no more translation?
   BEQ     buffer_char                                   ; just output the char

   CMN     R8, #1                                        ; is it -1 ?
   MVNEQ   R8, #4                                        ; if so, use code -5
   BEQ     text_printer_char                             ; to translate the character

   LDRB    R7, [R8, #4]                                  ; check this char
   TEQ     R0, R7                                        ; if it matches
   ADDEQ   R8, R8, #5                                    ; point to the length byte
   BEQ     buffer_string
   LDR     R8, [R8]                                      ; move down the link
   B       tc_00                                         ; and try again

.fn_space_flag
   FNdebug("fn_space_flag             ")
   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_space_flag%]           ; clear the space flag
   STMFD   R13!, {LR}
   BL      unbget                                        ; and always go back a char
   LDMFD   R13!, {LR}
   TEQ     R0, #code_return_char                         ; if the current char is
   TEQNE   R0, #code_line_char                           ; one of these
   TEQNE   R0, #code_cond_page                           ; then we need to process
   TEQNE   R0, #code_uncond_page                         ; them again!
   MOVEQ   PC, LR                                       ; so discard the trailing space
   ; otherwise
   LDR     R7, [psup, #CONST_psup_text%]
   AND     R7, R7, #%1000                                ; multi-column?
   LDR     R8, [tpub, #CONST_tpub_cpos%]
   LDR     R6, [tpub, #CONST_tpub_xc%]
   CMP     R8, R6                                        ; have we gone too far?
   MOVLT   R7, #0                                        ; no - clear the flag
   TEQ     R7, #0
   MOVNE   R8, #1                                        ; otherwise set the line flag
   STRNE   R8, [tpub, #CONST_tpub_line_flag%]
   BNE     unbget                                        ; and do this char again
   MOV     R0, #32
   B       text_trans

.fn_split_flag
   FNdebug("fn_split_flag             ")
   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_split_flag%]
   STMFD   R13!, {LR}
   BL      unbget
   LDMFD   R13!, {LR}
   MVN     R8, #7                                        ; code -8
   B       text_printer_string

.fn_escape_1
   FNdebug("fn_escape_1               ")
   AND     R7, R0, #&C0
   TEQ     R7, #&C0
   MOVEQ   R8, #2                                        ; start of literal sequence
   STREQ   R8, [tpub, #CONST_tpub_escape_flag%]
   MOVEQ   PC, LR

   ANDS    R7, R0, #&80
   MOVEQ   R8, #0                                        ; ignore unexplained escape
   STREQ   R8, [tpub, #CONST_tpub_escape_flag%]
   MOVEQ   PC, LR

   MVN     R8, #8                                        ; code -9
   STMFD   R13!, {LR}                                   ; style change
   BL      text_printer_char
   LDMFD   R13!, {LR}
   STR     R0, [tpub, #CONST_tpub_style_bits%]
   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_escape_flag%]
   MOV     PC, LR

.fn_escape_2
   FNdebug("fn_escape_2               ")
   TEQ     R0, #27
   MOVEQ   R8, #4
   STREQ   R8, [tpub, #CONST_tpub_escape_flag%]          ; expecting end of literal sequence
   MOVEQ   PC, LR

   TEQ     R0, #ASC"|"
   MOVEQ   R8, #3
   STREQ   R8, [tpub, #CONST_tpub_escape_flag%]
   MOVEQ   PC, LR

   CMP     R0, #32                                       ; if char >=32 and char <>127
   MOVLT   PC, LR                                       ; stuff char into buffer
   CMP     R0, #127
   MOVEQ   PC, LR
   B       buffer_char

.fn_escape_3
   FNdebug("fn_escape_3               ")
   MOV     R8, #2
   STR     R8, [tpub, #CONST_tpub_escape_flag%]
   TEQ     R0, #ASC"|"
   BEQ     buffer_char
   TEQ     R0, #ASC"?"
   MOVEQ   R0, #127
   BEQ     buffer_char
   AND     R0, R0, #&1F
   B       buffer_char

.fn_escape_4
   FNdebug("fn_escape_4               ")
   AND     R8, R0, #&C0
   TEQ     R8, #&C0                                      ; end of sequence?
   MOVEQ   R8, #2                                        ; if yes, move back to stage 2
   MOVNE   R8, #0                                        ; if not, no more escaping
   STR     R8, [tpub, #CONST_tpub_escape_flag%]
   MOV     PC, LR

.fn_page_flag
; on entry, R8 holds page_flag ...
   FNdebug("fn_page_flag              ")
   TEQ     R8, #1
   BNE     fn_page_flag_a
   CMP     R0, #127                                      ; conditional page throw
   BGT     fn_page_flag_b
   SUB     R0, R0, #16
   LDR     R7, [tpub, #CONST_tpub_line_max%]
   LDR     R8, [tpub, #CONST_tpub_line_page%]
   SUB     R7, R7, R8
   CMP     R0, R7
   BLE     fn_page_flag_b
   MOV     R8, #2                                        ; change to unconditional
   STR     R8, [tpub, #CONST_tpub_page_flag%]
   MOV     PC, LR

.fn_page_flag_b
   FNdebug("fn_page_flag_b            ")
   MOV     R8, #0                                        ; clear the page flag
   STR     R8, [tpub, #CONST_tpub_page_flag%]
   MOV     R8, #1                                        ; and move down a line
   STR     R8, [tpub, #CONST_tpub_line_flag%]
   MOV     PC, LR

.fn_page_flag_a
   FNdebug("fn_page_flag_a            ")
   STMFD   R13!, {LR}                                   ; we're going to be calling some subs
   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_page_flag%]
   MVN     R8, #16                                       ; code -17
   BL      text_printer_string
   LDR     R8, [tpub, #CONST_tpub_manual_flag%]          ; if the printer requires page feeding
   TEQ     R8, #0                                        ; set the flag (ie the offset to the formfeed) to say so
   LDRNE   R8, [queu, #CONST_queu_buff_in%]
   STRNE   R8, [tpub, #CONST_tpub_pause_flag%]           ; pause the printer!
   BL      start_page
   LDMFD   R13!, {LR}
   B       unbget

.fn_format_flag
   FNdebug("fn_format_flag            ")
   LDR     R8, [tpub, #CONST_tpub_line_flag%]            ; if line_flag<>0
   TEQ     R8, #0
   BEQ     fn_format_flag_a
   TEQ     R8, #1                                        ; if line_flag=1
   BEQ     fn_format_flag_b
   EOR     R8, R8, R0
   TEQ     R8, #7                                        ; or line_flag EOR char <> 10 EOR 13
   BNE     fn_format_flag_b
   MOV     R8, #1                                        ; otherwise
   STR     R8, [tpub, #CONST_tpub_line_flag%]            ; line_flag = 1
   MOV     PC, LR

.fn_format_flag_b
; if line_flag = 1 or (line_flag EOR char) <> (10 EOR 13)
   FNdebug("fn_format_flag_b          ")
   STMFD   R13!, {LR}
   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_line_flag%]
   LDR     R8, [tpub, #CONST_tpub_line_doc%]
   TEQ     R8, #0
   BNE     fn_format_flag_b_a
   TEQ     R0, #code_format_char
   BEQ     fn_format_flag_b_a
; if line_doc = 0 and char <> format_char
   BL      start_page                                    ; starting the document
   B       fn_format_flag_b_c

.fn_format_flag_b_a
; else if char <> cond_page and char <> uncond_page
   FNdebug("fn_format_flag_b_a        ")
   TEQ     R0, #code_cond_page
   TEQNE   R0, #code_uncond_page
   BEQ     fn_format_flag_b_c
   LDR     R8, [tpub, #CONST_tpub_format_flag%]          ; if format_flag = 4
   TEQ     R8, #4
   BEQ     fn_format_flag_b_b
   TEQ     R8, #3                                        ; or format_flag <> 3
   BEQ     fn_format_flag_b_d
   TEQ     R0, #code_format_char                         ; and char <> format
   BEQ     fn_format_flag_b_d
   LDR     R8, [tpub, #CONST_tpub_line_page%]            ; and line_page >= line_max
   LDR     R6, [tpub, #CONST_tpub_line_max%]
   CMP     R8, R6
   BLT     fn_format_flag_b_d
.fn_format_flag_b_b
   FNdebug("fn_format_flag_b_b        ")
   MOV     R8, #2                                        ; then page_flag = 2
   STR     R8, [tpub, #CONST_tpub_page_flag%]
   B       fn_format_flag_b_c

.fn_format_flag_b_d
   FNdebug("fn_format_flag_b_d        ")
   LDR     R8, [tpub, #CONST_tpub_format_flag%]          ; if format_flag = 2
   TEQ     R8, #2
   BNE     fn_format_flag_b_c
   TEQ     R0, #code_format_char                         ; if char <> format
   BEQ     fn_format_flag_b_c
   LDR     R8, [tpub, #CONST_tpub_line_epilogue%]
   BL      buffer_string
   BL      start_line

.fn_format_flag_b_c
   FNdebug("FNformat_flag_b_c         ")
   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_format_flag%]
   LDMFD   R13!, {LR}
   B       unbget

.fn_format_flag_a
   FNdebug("fnformat_flag_a           ")
   TEQ     R0, #code_line_char
   TEQNE   R0, #code_return_char
   STREQ   R0, [tpub, #CONST_tpub_line_flag%]
   MOVEQ   PC, LR

   LDR     R8, [tpub, #CONST_tpub_format_flag%]
   TEQ     R8, #1
   MOVNE   PC, LR

   TEQ     R0, #code_ruler_char
   MOVEQ   R8, #0
   STREQ   R8, [tpub, #CONST_tpub_format_flag%]
   STREQ   R8, [tpub, #CONST_tpub_ruler_line%]
   MOVEQ   R8, #1
   STREQ   R8, [tpub, #CONST_tpub_ruler_flag%]
   MOVEQ   R8, #1000
   STREQ   R8, [tpub, #CONST_tpub_left_new%]
   STREQ   R8, [tpub, #CONST_tpub_right_new%]
   MOVEQ   PC, LR

   TEQ     R0, #code_layout_char
   BNE     fn_format_flag_a_b
   LDR     R8, [tpub, #CONST_tpub_line_doc%]             ; if line_num = 0
   TEQ     R8, #0
   STREQ   R8, [tpub, #CONST_tpub_format_flag%]
   LDREQ   R7, [tpub, #CONST_tpub_layout_line%]
   STREQ   R8, [R7]                                      ; zero length string
   STREQ   R8, [R7, #4]                                  ; that's 8 bytes cleared
   STREQ   R8, [R7, #8]                                  ; that's 12 bytes cleared
   MOVEQ   R8, #1
   STREQ   R8, [tpub, #CONST_tpub_layout_flag%]
   MOV     PC, LR

.fn_format_flag_a_b
   FNdebug("fn_format_flag_a_b        ")
   TEQ     R0, #code_footstart_char
   MOVEQ   R8, #0
   STREQ   R8, [tpub, #CONST_tpub_format_flag%]
   STREQ   R8, [tpub, #CONST_tpub_footnote_num%]
   SUBEQ   R8, R8, #1
   STREQ   R8, [tpub, #CONST_tpub_footnote_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_footend_char
   MOVEQ   R8, #4
   MOVNE   R8, #2
   STR     R8, [tpub, #CONST_tpub_format_flag%]
   MOV     PC, LR

.fn_ruler_flag
   FNdebug("fn_ruler_flag             ")
   LDR     R8, [tpub, #CONST_tpub_line_flag%]
   TEQ     R8, #0
   BEQ     fn_ruler_flag_a
   TEQ     R8, #1                                        ; if line_flag=1
   BEQ     fn_ruler_flag_b
   EOR     R8, R8, R0
   TEQ     R8, #7                                        ; or line_flag EOR char <> 10 EOR 13
   BNE     fn_ruler_flag_b
   MOV     R8, #1                                        ; otherwise
   STR     R8, [tpub, #CONST_tpub_line_flag%]            ; line_flag = 1
   MOV     PC, LR

.fn_ruler_flag_b
; line_flag = 1 or (line_flag eor char)<>(10 eor 13)
   FNdebug("fn_ruler_flag_b           ")
   LDR     R8, [tpub, #CONST_tpub_ruler_flag%]
   CMP     R8, #3
   BLE     fn_ruler_flag_b_a
   LDR     R8, [tpub, #CONST_tpub_ruler_line%]           ; we want the third byte
   MOV     R0, R8, LSR #16                               ; 'cos we store the ruler
   AND     R0, R0, #255                                  ; as a word of 4 chars
   SUB     R0, R0, #48
   CMP     R0, #0
   BLT     fn_ruler_flag_b_a
   CMP     R0, #3
   STMFD   R13!, {LR}
   MVNLE   R8, #12                                       ; code -13
   BLLE    text_printer_char
   LDMFD   R13!, {LR}

.fn_ruler_flag_b_a
   FNdebug("fn_ruler_flag_b_a         ")
   MOV     R8, #0                                        ; ruler_flag = 0
   STR     R8, [tpub, #CONST_tpub_ruler_flag%]
   MOV     R8, #2                                        ; format_flag = 2
   STR     R8, [tpub, #CONST_tpub_format_flag%]
   B       unbget

.fn_ruler_flag_a
   FNdebug("fn_ruler_flag_a           ")
   TEQ     R0, #code_line_char
   TEQNE   R0, #code_return_char
   STREQ   R0, [tpub, #CONST_tpub_line_flag%]
   MOVEQ   PC, LR

   LDR     R8, [tpub, #CONST_tpub_ruler_flag%]           ; if char = "["
   TEQ     R0, #ASC"["                                   ; left_new = ruler_flag-1
   SUBEQ   R8, R8, #1
   STREQ   R8, [tpub, #CONST_tpub_left_new%]
   ADDEQ   R8, R8, #1

   TEQ     R0, #ASC"]"                                   ; if char = "]"
   STREQ   R8, [tpub, #CONST_tpub_right_new%]            ; right_new = ruler_flag

   ADD     R8, R8, #1                                    ; ruler_flag += 1
   STR     R8, [tpub, #CONST_tpub_ruler_flag%]
   LDR     R8, [tpub, #CONST_tpub_ruler_line%]           ; ruler_line += char
   ADD     R8, R0, R8, LSL #8
   STR     R8, [tpub, #CONST_tpub_ruler_line%]
   MOV     PC, LR

.fn_layout_flag
   FNdebug("fn_layout_flag            ")
   LDR     R8, [tpub, #CONST_tpub_line_flag%]
   TEQ     R8, #0
   BEQ     fn_layout_flag_a
   TEQ     R8, #1                                        ; if line_flag=1
   BEQ     fn_layout_flag_b
   EOR     R8, R8, R0
   TEQ     R8, #7                                        ; or line_flag EOR char <> 10 EOR 13
   BNE     fn_layout_flag_b
   MOV     R8, #1                                        ; otherwise
   STR     R8, [tpub, #CONST_tpub_line_flag%]            ; line_flag = 1
   MOV     PC, LR

.fn_layout_flag_b
; line_flag = 1 or (line_flag eor char)<>(10 eor 13)
   FNdebug("fn_layout_flag_b          ")
   STMFD   R13!, {R5,LR}
   LDR     R5, [magic, #magic_task_buff]                 ; get the ptr to task_buff
   LDR     R8, [tpub, #CONST_tpub_layout_line%]          ; get the ptr to the string
   LDRB    R7, [R8, #1]
   LDRB    R6, [R8, #2]
   BL      fn_val
   STR     R6, [R5, #8]                                  ; layout height = val(left$,2)

   LDRB    R7, [R8, #3]                                  ; layout top =
   LDRB    R6, [R8, #4]                                  ; val(mid$,3,2) +
   BL      fn_val
   STR     R6, [R5, #12]
   LDRB    R7, [R8, #5]                                  ; val(mid$,5,2)
   LDRB    R6, [R8, #6]
   BL      fn_val
   LDR     R7, [R5, #12]
   ADD     R6, R6, R7
   STR     R6, [R5, #12]

   LDRB    R7, [R8, #7]                                  ; layout bottom =
   LDRB    R6, [R8, #8]                                  ; val(mid$,7,2) +
   BL      fn_val
   STR     R6, [R5, #16]
   LDRB    R7, [R8, #9]                                  ; val(mid$,9,2)
   LDRB    R6, [R8, #10]
   BL      fn_val
   LDR     R7, [R5, #16]
   ADD     R6, R6, R7
   STR     R6, [R5, #16]

   MVN     R8, #17                                       ; code -18
   BL      text_printer_string

   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_layout_flag%]
   MOV     R8, #2
   STR     R8, [tpub, #CONST_tpub_format_flag%]
   LDMFD   R13!, {R5,LR}
   B       unbget

.fn_layout_flag_a
   FNdebug("fn_layout_flag_a          ")
   TEQ     R0, #code_line_char
   TEQNE   R0, #code_return_char
   STREQ   R0, [tpub, #CONST_tpub_line_flag%]
   MOVEQ   PC, LR

   LDR     R8, [tpub, #CONST_tpub_layout_line%]          ; get the ptr
   LDRB    R7, [R8]                                      ; get the length
   CMP     R7, #10                                       ; and store if less than 10 chars
   ADDLT   R7, R7, #1
   STRLTB  R0, [R8, R7]
   STRLTB  R7, [R8]
   MOV     PC, LR

.fn_line_flag
; routine enters with R8 holding the line flag
   FNdebug("fn_line_flag              ")
   TEQ     R8, #1                                        ; if line_flag=1
   BEQ     fn_line_flag_a
   EOR     R8, R8, R0
   TEQ     R8, #7                                        ; or line_flag EOR char <> 10 EOR 13
   BNE     fn_line_flag_a
   STR     R0, [tpub, #CONST_tpub_line_flag%]            ; otherwise line_flag = char
   MOV     PC, LR

.fn_line_flag_a
   FNdebug("fn_line_flag_a            ")
   STMFD   R13!, {LR}
   MOV     R8, #0
   STR     R8, [tpub, #CONST_tpub_line_flag%]

   LDR     R8, [tpub, #CONST_tpub_cc_print%]             ; if cc_print <> 0
   TEQ     R8, #0
   BNE     fn_line_flag_a_d                              ; then do it

; Bug-fix; don't test for code_uncond_page 'cos this causes problems if
; we haven't output anything yet - you don't get the page heading
;  TEQ     R0, #code_uncond_page                         ; or char = uncond_page
   TEQ     R0, #code_cond_page                           ; or char = cond_page
   TEQNE   R0, #code_format_char                         ; or char = format_char
   BEQ     fn_line_flag_a_b                              ; then don't do anything

.fn_line_flag_a_d
   LDR     R8, [tpub, #CONST_tpub_line_doc%]
   TEQ     R8, #0
   BNE     fn_line_flag_a_c
   BL      start_page                                    ; we are just starting ...
   B       fn_line_flag_a_b

.fn_line_flag_a_c
   FNdebug("fn_line_flag_a_c          ")
   LDR     R8, [tpub, #CONST_tpub_line_epilogue%]
   BL      buffer_string
   LDR     R8, [tpub, #CONST_tpub_line_page%]            ; do the end of page test
   LDR     R6, [tpub, #CONST_tpub_line_max%]             ; AFTER we've ended this line
   CMP     R8, R6                                        ; but BEFORE we've started the
   MOVGE   R8, #2                                        ; next one.
   STRGE   R8, [tpub, #CONST_tpub_page_flag%]
   BGE     fn_line_flag_a_b
   BL      start_line

.fn_line_flag_a_b
   FNdebug("fn_line_flag_a_b          ")
   LDMFD   R13!, {LR}
   B       unbget

.fn_footnote_flag
; routine enters with R8 holding the footnote flag
   FNdebug("fn_footnote_flag          ")
   CMP     R8, #0
   BGT     fn_footnote_flag_a

   TEQ     R0, #code_line_char
   TEQNE   R0, #code_return_char
   MOVEQ   R8, #0
   STREQ   R8, [tpub, #CONST_tpub_footnote_flag%]
   MOVEQ   R8, #3
   STREQ   R8, [tpub, #CONST_tpub_format_flag%]
   BEQ     unbget

   LDR     R6, [tpub, #CONST_tpub_footnote_num%]
   MOV     R7, R6, LSL #3                                ; R7 = number * 8
   ADD     R7, R7, R6, LSL #1                            ; R7 = number * 10
   ADD     R7, R7, R0
   SUB     R7, R7, #48
   STR     R7, [tpub, #CONST_tpub_footnote_num%]
   ; R8 still holds footnote_flag
   CMN     R8, #2                                        ; flag < -2 ?
   SUBGE   R8, R8, #1                                    ; no - sub 1
   STRGE   R8, [tpub, #CONST_tpub_footnote_flag%]
   MOVGE   PC, LR

   MOV     R8, #0                                        ; yes
   STR     R8, [tpub, #CONST_tpub_footnote_flag%]
   STMFD   R13!, {LR}
   MVN     R8, #10                                       ; code -11
   BL      text_printer_string
   LDMFD   R13!, {LR}
   MOV     R8, #3
   STR     R8, [tpub, #CONST_tpub_format_flag%]
   MOV     PC, LR

.fn_footnote_flag_a
   FNdebug("fn_footnote_flag_a        ")
   TEQ     R0, #ASC","
   MOVEQ   R8, #2                                        ; expect footnote number to follow
   STREQ   R8, [tpub, #CONST_tpub_footnote_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_footnote_char
   MOVEQ   R8, #0
   STREQ   R8, [tpub, #CONST_tpub_footnote_flag%]
   MOVEQ   PC, LR

   TEQ     R0, #code_line_char
   TEQNE   R0, #code_return_char
   MOVEQ   R8, #0
   STREQ   R8, [tpub, #CONST_tpub_footnote_flag%]
   STREQ   R0, [tpub, #CONST_tpub_line_flag%]
   MOVEQ   PC, LR

   ; R8 still holds footnote_flag
   TEQ     R8, #2
   STMFD   R13!, {LR}
   ; note that the original sources used FNstring_trans, but
   ; since that is the only call for it, there seems little point
   BLEQ    translate_character
   LDMFD   R13!, {LR}
   MOV     PC, LR

.start_line
   FNdebug("start_line                ")
   TEQ     R0, #code_format_char
   BNE     start_line_a
   LDR     R8, [tpub, #CONST_tpub_cc_print%]
   TEQ     R8, #0
   STREQ   R8, [tpub, #CONST_tpub_cpos%]
   MOVEQ   PC, LR

.start_line_a
   FNdebug("start_line_a              ")

; line_page and line_doc MUST be incremented in the back-end code
;  LDR     R8, [tpub, #CONST_tpub_line_page%]
;  ADD     R8, R8, #1
;  STR     R8, [tpub, #CONST_tpub_line_page%]
;  LDR     R8, [tpub, #CONST_tpub_line_doc%]
;  ADD     R8, R8, #1
;  STR     R8, [tpub, #CONST_tpub_line_doc%]
   MVN     R8, #9                                        ; code -10
   B       text_printer_string

.start_page
   FNdebug("start_page                ")
   STMFD   R13!, {LR}
   MVN     R8, #18                                       ; code -19
   BL      text_printer_string
   LDMFD   R13!, {LR}
   B       start_line

.buffer_char
; places the character in R0 into the output buffer
   FNdebug("buffer_char               ")
   STMFD   R13!, {R0,R7,R8}
   LDR     R7, [queu, #CONST_queu_tbff%]
   LDR     R8, [queu, #CONST_queu_buff_in%]
   STRB    R0, [R7, R8]
   ADD     R8, R8, #1
   STR     R8, [queu, #CONST_queu_buff_in%]
   LDMFD   R13!, {R0,R7,R8}
   MOV     PC, LR

.buffer_string
; places the GSTransed string at R8 into the output buffer
   FNdebug("buffer_string             ")
   TEQ     R8, #0                                        ; is this a valid string?
   MOVEQ   PC, LR                                       ; no - ignore it

   STMFD   R13!, {R0,R5-R8}
   LDR     R7, [queu, #CONST_queu_tbff%]
   LDR     R6, [queu, #CONST_queu_buff_in%]
   LDRB    R5, [R8], #1
.buffer_string_a
   TEQ     R5, #0
   STREQ   R6, [queu, #CONST_queu_buff_in%]
   LDMEQFD R13!, {R0,R5-R8}
   MOVEQ   PC, LR

   LDRB    R0, [R8], #1
   STRB    R0, [R7, R6]
   ADD     R6, R6, #1
   SUB     R5, R5, #1                                    ; we can't use conditions 'cos
   B       buffer_string_a                               ; the string might be zero lngth

.fn_val
; takes the characters in R6 (LS) and R7 (MS) and
; returns the value in R6
   FNdebug("fn_val                    ")
   CMP     R6, #ASC"0"                                   ; if R6 < "0"
   CMPGE   R7, #ASC"0"                                   ; or R7 < "0"
   MOVLT   R6, #0
   MOVLT   PC, LR

   CMP     R6, #ASC"9"                                   ; if R6 > "9"
   CMPLE   R7, #ASC"9"                                   ; or R7 > "9"
   MOVGT   R6, #0
   MOVGT   PC, LR

   SUB     R6, R6, #48
   SUB     R7, R7, #48
   ADD     R6, R6, R7, LSL #3                            ; R6 = R6 + R7*8
   ADD     R6, R6, R7, LSL #1                            ; R6 = R6 + R7*10
   MOV     PC, LR

.text_printer_char
   FNdebug("text_printer_char         ")
   STMFD   R13!, {R5,LR}
   LDR     R5, [magic, #magic_task_buff]
   STR     R0, [R5, #8]
   BL      text_printer_string
   LDMFD   R13!, {R5, PC}

.text_printer_string
; asks the specified back-end to execute the code in R8
; then copies the result into the output buffer
   FNdebug("text_printer_string       ")
   STMFD   R13!, {R5,LR}
   LDR     R5, [magic, #magic_task_buff]
   STR     R8, [R5, #0]
   STR     queu, [R5, #4]
   BL      printer_reason_code                           ; always does p_r_c -8
   ADD     R8, R5, #8                                    ; result is at task_buff+8
   BL      buffer_string
   LDMFD   R13!, {R5,PC}

.unbget
   FNdebug("unbget                    ")
   STMFD   R13!, {R0-R2,LR}
   MOV     R0, #0
   LDR     R1, [queu, #CONST_queu_handle_in%]
   SWI     "XOS_Args"
   SUB     R2, R2, #1
   MOV     R0, #1
   SWI     "XOS_Args"
   LDMFD   R13!, {R0-R2,PC}

.printer_reason_code
; call the BASIC routine to execute a reason code
   FNdebug("printer_reason_code       ")
   STMFD   R13!, {R0-R12,LR}
   LDR     R8, [magic, #magic_argp]
   LDR     R10, [magic, #magic_r14]
   LDR     R11, [magic, #magic_reasn_string]
   ADR     LR, printer_reason_code_a
   ADD     PC, R10, #&44                                 ; do it!
.printer_reason_code_a
   LDMFD   R13!, {R0-R12,PC}

.terminate_lines
; zip through some store, turning all control characters into 13's
;  R1 -> block
;  R2 =  size of block
   FNdebug("terminate_lines           ")
   STMFD   R13!, {R0-R4,LR}
   ;R0 is loop counter
   ;R3 is a 13
   ;R4 is the character at the current position
   MOV     R3, #13
   MOV     R0, #0
.terminate_lines_a
   CMP     R0, R2
   BGE     terminate_lines_b
   LDRB    R4, [R1, R0]
   CMP     R4, #32
   STRLTB  R3, [R1, R0]
   ADD     R0, R0, #1
   B       terminate_lines_a
.terminate_lines_b
   LDMFD   R13!, {R0-R4,PC}

.match_line
.match_any_line

.rtoz

; converts a 0-terminated string into a BASIC string

; Entry
;       R8       = ARGP
;      [R9, #0]  = lvalue of result string
;      [R9, #4]  = type of result string
;      [R9, #8]  = lvalue of pointer to string to convert
;      [R9, #12] = type of pointer to string to convert
;       R11      -> STRACC
;       R12      -> LINE

        STMFD   R13!, {LR}
        MOV     R10, LR
; R10 -> BASIC entry points

        LDR     R3, [R9, #8]
; R3 = lvalue of pointer to string to convert
        LDR     R3, [R3]
; R3 -> string to convert

        ;Copy the string to STRACC
        MOV     R2, #0 
; R2 = string counter
.rtoz_loop
        LDRB    R0, [R3, R2]
        TEQ     R0, #0
        STRNEB  R0, [R11, R2]
        ADDNE   R2, R2, #1
        BNE     rtoz_loop
        
        ADD     R2, R11, R2
; R2 -> end of string
        MOV     R3, R11
; R3 -> start of string
        LDR     R4, [R9, #0]
; R4 -> lvalue of result string

        ADR     LR, rtoz_return
        ADD     PC, R10, #STSTORE
.rtoz_return

        ;Return
        LDMFD   R13!, {PC}

.ctoz

; converts a byte-counted string into a BASIC string

; Entry
;       R8 = ARGP
;       [R9, #0]  = lvalue of result string
;       [R9, #4]  = type of result string
;       [R9, #8]  = lvalue of pointer to string to convert
;       [R9, #12] = type of pointer to string to convert
;       R11 -> string accumulator
;       R12 -> LINE

        STMFD   R13!, {LR}
        MOV     R10, LR
; R10 -> BASIC entry points

        LDR     R3, [R9, #8]
; R3 = lvalue of pointer to string to convert
        LDR     R3, [R3]
; R3 -> string to convert
        LDRB    R1, [R3, #0]
; R1 = length of string

        ;Copy the string to STRACC
        ADD     R3, R3, #1
        MOV     R2, #0
; R2 = string counter
.ctoz_loop
        LDRB    R0, [R3, R2]
        CMP     R2, R1
        STRLTB  R0, [R11, R2]
        ADDLT   R2, R2, #1
        BLT     ctoz_loop

        ADD     R2, R11, R2
; R2 -> end of string
        MOV     R3, R11
; R3 -> start of string
        LDR     R4, [R9, #0]
; R4 -> lvalue of result string

        ADR     LR, ctoz_return
        ADD     PC, R10, #STSTORE
.ctoz_return

        ;Return
        LDMFD   R13!, {PC}
.end_of_code_label
]
NEXT
SYS"OS_File",10,"DataFiles.Code",&FFD,,code%,O%
PRINT"Support code assembled - ";(O%-code%); " bytes long"
IF trace% THEN PRINT "ASSEMBLED WITH DEBUG WRITES"
END

DEF FNtrace(s$)
   LOCAL j%
   IF trace% THEN
      [  OPT i%
         STMFD   R13!, {R0}
         SWI     256+4
         MOV     R0, #ASC ">"
         SWI     "OS_WriteC"
      ]
      FOR j%=1 TO LEN s$
         [  OPT i%
            MOV     R0, #ASC MID$(s$,j%,1)
            SWI     "OS_WriteC"
         ]
      NEXT
      [  OPT i%
         MOV     R0, #10
         SWI     "OS_WriteC"
         MOV     R0, #13
         SWI     "OS_WriteC"
         LDMFD   R13!, {R0}
      ]
   ENDIF
=0

DEF FNtrace_reg(r%)
   LOCAL loop1%,end1%,loop2%,end2%
   IF trace% THEN
      [  OPT i%
         STMFD   R13!, {R0, R1, R2}

         STMFD   R13!, {r%}

         MOV     R0, #ASC ">"
         SWI     "OS_WriteC"

         MOV     R0, #ASC "R"
         SWI     "OS_WriteC"

         MOV     R0, #r%
         ADR     R1, P%+4*33 ;workspace
         MOV     R2, #32
         SWI     "OS_ConvertInteger4"

         MOV     R1, R0

;loop1
         LDRB    R0, [R1]
         TEQ     R0, #0
         BEQ     P%+4*4 ;end1
         SWI     "OS_WriteC"
         ADD     R1, R1, #1
         B       P%-4*5 ;loop1
;end1

         MOV     R0, #ASC ":"
         SWI     "OS_WriteC"
         MOV     R0, #ASC " "
         SWI     "OS_WriteC"
         MOV     R0, #ASC "&"
         SWI     "OS_WriteC"

         LDMFD   R13!, {R0}
         ADR     R1, P%+4*16 ;workspace
         MOV     R2, #32
         SWI     "OS_ConvertHex8"

         MOV     R1, R0

;loop2
         LDRB    R0, [R1]
         TEQ     R0, #0
         BEQ     P%+4*4 ;end2
         SWI     "OS_WriteC"
         ADD     R1, R1, #1
         B       P%-4*5 ;loop2
;end2

         MOV     R0, #10
         SWI     "OS_WriteC"
         MOV     R0, #13
         SWI     "OS_WriteC"
         LDMFD   R13!, {R0, R1, R2}

         B       P%+4*9 ;next
;workspace
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
;next
      ]
   ENDIF
=0

DEF FNtrace_memory(r%,o%)
   IF trace% THEN
      [  OPT i%
         STMFD   R13!, {R0, R1, R2}

         MOV     R0, #ASC "*" 
         SWI     "OS_WriteC"
         MOV     R0, #ASC " "
         SWI     "OS_WriteC"
         MOV     R0, #ASC "&"
         SWI     "OS_WriteC"

         LDR     R0, [r%,#o%]
         ADR     R1, P%+4*16 ;workspace
         MOV     R2, #32
         SWI     "OS_ConvertHex8"

         MOV     R1, R0

;loop1
         LDRB    R0, [R1]
         TEQ     R0, #0
         BEQ     P%+4*4 ;end1
         SWI     "OS_WriteC"
         ADD     R1, R1, #1
         B       P%-4*5 ;loop1
;end1

         MOV     R0, #10
         SWI     "OS_WriteC"
         MOV     R0, #13
         SWI     "OS_WriteC"
         LDMFD   R13!, {R0, R1, R2}

         B       P%+4*9 ;next
;workspace
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
         EQUD    0
;next
      ]
   ENDIF
=0

DEFFNws(n%)
p%+=n%
=(p%-n%)
:
REM note that FNdebug just prints a string. you will need a spool
REM file open or VDU4 done previously
:
DEFFNdebug(s$)
[ OPT i%
REM    STMFD   R13!, {LR}
REM    SWI     "OS_WriteS"
REM    EQUS    s$
REM    EQUB    13
REM    EQUB    10
REM    EQUB    0
REM    ALIGN
REM    LDMFD   R13!, {LR}
]
=0
:
DEFFNshowerror
LOCAL dummy
[ OPT i%
   bvc     dummy
   stmfd   r13!,{r0}
   add     r0, r0, #4
   swi     "OS_Write0"
   ldmfd   r13!,{r0}
.dummy
]
=0
:
DEFFNreport(string$)
LOCAL regprint, printdigits
[ OPT i%
   stmfd   r13!,{pc}
   stmfd   r13,{r0-r14}
   sub     r13,r13,#15*4

   swi     256+4
   swi     256+26
   swi     256+17
   swi     256+7
   swi     256+17
   swi     256+128+0

   swi     "OS_NewLine"

   add     r11,r13,#0
   mov     r10,#0

.regprint
   swi     256+32
   swi     256+ASC"R"
   cmp     r10,#10
   addhs   r0,r10,#ASC"A"-10
   addlo   r0,r10,#ASC"0"
   swi     "OS_WriteC"
   swi     256+ASC"="

   ldr     r0,[r11],#4
   cmp     r10,#13
   addeq   r0,r0,#4

   stmfd   r13!,{r0-r1}
   mov     r0,r0,ror #28
   mov     r1,#8

.printdigits
   stmfd   r13!,{r0}

   and     r0,r0,#15
   cmp     r0,#10
   addcs   r0,r0,#7
   add     r0,r0,#ASC"0"
   swi     "OS_WriteC"

   ldmfd   r13!,{r0}

   mov     r0,r0,ror #28
   subs    r1,r1,#1
   bne     printdigits

   ldmfd   r13!,{r0-r1}

   teq     r10,#14
   teqne   r10,#15
   bne     (P%+(4*33))

   swi     &100+32
   swi     &100+ASC"("

   tst     r0,#1<<31
   swine   &100+ASC"N"
   swieq   &100+32
   tst     r0,#1<<30
   swine   &100+ASC"Z"
   swieq   &100+32
   tst     r0,#1<<29
   swine   &100+ASC"C"
   swieq   &100+32
   tst     r0,#1<<28
   swine   &100+ASC"V"
   swieq   &100+32

   tst     r0,#1<<27
   swieq   &100+ASC"i"
   swine   &100+32
   tst     r0,#1<<26
   swieq   &100+ASC"f"
   swine   &100+32

   swi     &100+ASC")"
   swi     &100+ASC" "
   swi     &100+ASC"("

   and     r0,r0,#%11
   adr     r1,P%+(4*5)

   add     r0,r1,r0,lsl #2
   swi     "OS_Write0"
   swi     &100+ASC")"

   b       (P%+(4*5))

   equs    "USR"+CHR$0
   equs    "FIQ"+CHR$0
   equs    "IRQ"+CHR$0
   equs    "SVC"+CHR$0
   align

   swi     &100+32

   swi     "OS_NewLine"
   add     r10,r10,#1
   cmp     r10,#16
   bne     regprint
]
IF string$<>"" THEN
[ OPT i%
   swi     "OS_WriteS"
   equs    "  "+STRING$(LEN(string$),CHR$32)+CHR$10+CHR$13
   equs    " "+string$+" "+CHR$10+CHR$13
   equs    "  "+STRING$(LEN(string$),CHR$32)+CHR$0
   align

   swi     "OS_ReadC"
   movcs   r0,#126
   swics   "OS_Byte"
]
ENDIF
[ OPT i%
   ldr     r14,[r13,#15*4]
   teqp    pc,r14
   ldmfd   r13!,{r0-r14}

   add     r13,r13,#4              ; phew!
]
=0

DEF FNddt_include
IF trace% THEN
[ OPT i%
.ddt_start
  STMDB r13!, {r0-r15}
  STR   r14, [r13, #15*4]
  ADD   r2, r13, #16*4
  STR   r2, [r13, #13*4]
  MOV   r0, #2
  ADR   r1, debugging_title
  ADR   r2, entry_dot_matrix
  LDR   r3, code_size_value
  MOV   r4, #0
  MOV   r5, #0
  MOV   r6, #0
  MOV   r7, #0
  MOV   r8, r13
  SWI   "Debugger_StartDebug"
  ; swi doesn't return
.code_size_value
  DCD   end_of_code_label - entry_dot_matrix
.debugging_title
  EQUS  "!Printers"
  DCB   0
  ALIGN
]
ENDIF
=0

DEF FNddt_start
IF trace% THEN
[ OPT i%
  STMFD R13!, {R14}
  BL    ddt_start
  LDMFD R13!, {R14}
]
ENDIF
=0

DEF FNddt_end
IF trace% THEN
[ OPT i%
  SWI   "Debugger_EndDebug"
]
ENDIF
=0
