*                  Sorcerer Editor/Word Processor Listing, DV1.2
*                  By Ian Mapleson (mapesdhs@yahoo.com)
*                  First Release: 22/Apr/1992
*                  Last Change: 04/Mar/1994

* Note1: A few abreviations used...

*        SRTBU means 'Save Registers To Be Used', which means that the specified registers are being placed on the stack to
*        prevent corruption.
*        RRU means 'Restore Register(s) Used', which means that the registers previously placed on the stack (probably with a MOVEM)
*        are now being restored to their original values.
*        TFM means 'Text File Memory', which refers to the area of memory that contains Sorcerer's textual data.
*        CS means 'Correct Stack', this will usually appear after a TRAP call or similar operation.
*        TWA means 'text window area' and refers to the part of the screen (below the white bar) where the user's text is shown.
* Note2: It may be that the asterisks for the comments aren't necessary, but they are a good visual aid to seperating the comments
*        from the code and so have been left in.

IntIn       EQU  8                          * These are line A related variables (e.g. see ScrnDsplay routine)...
InitA       EQU  $A000
ShowMouse   EQU  $A009
HideMouse   EQU  $A00A
TrnsMouse   EQU  $A00B
FilledRect  EQU  $A005
UpArrowChr  EQU  1                          * ASCII code constants used in ScrnDsplay...
DnArrowChr  EQU  2
ContrlCKey  EQU  $2E                        * These are the key scancode constants (e.g. see EnterLine). Note that though some
PosRemKey   EQU  $13                        * have the same value (e.g. ControlCKey and CancKey), this is because the constants
JmpToRmKey  EQU  $24                        * may be used in different places in different ways (ControlCKey used in MainEdLoop,
ClearRmKey  EQU  $12                        * CancKey used in TosError)...
TpOfTxtKey  EQU  $14
BtOfTxtKey  EQU  $30
CenterKey   EQU  $32
CancKey     EQU  $2E
AlterKey    EQU  $1E
RetryKey    EQU  $13
LftCrsrKey  EQU  $4B
RtCrsrKey   EQU  $4D
UpCrsrKey   EQU  $48
DnCrsrKey   EQU  $50
InvertKey   EQU  $1
YesKey      EQU  $15
NoKey       EQU  $31
HelpKey     EQU  $62
ClrFileKey  EQU  $2E
DeleteKey   EQU  $53
BackspcKey  EQU  $E
InsertKey   EQU  $52
ClrHomeKey  EQU  $47
ToMidLftKy  EQU  $77
UndoKey     EQU  $61
ReturnKey   EQU  $1C
EscKey      EQU  $1
KillLnKey   EQU  $25
DelLnKey    EQU  $15
LnStartKey  EQU  $73
LineEndKey  EQU  $74
SaveKey     EQU  $1F
LoadKey     EQU  $26
GotoLnKey   EQU  $22
OverflwKey  EQU  $18
WordProKey  EQU  $11
QuitKey     EQU  $10
CancelOpn   EQU  $2E
RetryOption EQU  $13
AlterOption EQU  $1E
RytShiftBit EQU  0                          * These are the constants used to test the various flag bits of KeybrdStat (see
LftShiftBit EQU  1                          * ScanKeybrd for details)...
ControlBit  EQU  2
AltBit      EQU  3
CapsLockBit EQU  4
EthrShftBit EQU  7
LeftWrdCode EQU  1                          * Simple codes used as flags (see RyteButAc, for example) in MainEdLoop & EnterLine...
RyteWrdCode EQU  2
DelWordCode EQU  3
UndoDWCode  EQU  4
Yes         EQU  $FF
No          EQU  0
Seeking     EQU  0                          * Disk file operation mode flags (FileOpMode is set to one of these) used, for
Creating    EQU  4                          * example, in TosError...
Opening     EQU  8
Loading     EQU  12
Saving      EQU  16
Closing     EQU  20
Decoding    EQU  24
DrvIdError  EQU  -127                       * Extended TOS error codes. The first four are for path/file name validation...
DrvSpecErr  EQU  -126                       * The third last (-123) is used to prevent loading of non-ASCII (e.g. .PRG) files.
PDFError    EQU  -125                       * The second last (-122) is used as an internal flag in GetDskFile to show when a file
PathError   EQU  -124                       * being read in from disk fills the entire TFM. No error is given on screen if this
IllegalChr  EQU  -123                       * happens, as it isn't really an error as such (just an allowable condition). The last
FullTFM     EQU  -122                       * code (-121) is for 'unexpected end of file' and is described in full at ErrCodeLst:
UnxpctdEof  EQU  -121                       * in this listing.
PathBufLen  EQU  70                         * Lengths of a couple of buffers...
FileBufLen  EQU  13
DeskSpec    EQU  $00000300                  * These constants define Hot Spots for mouse detection (for details, see QHotSpot
CancelSpec  EQU  $440C4A0C                  * and SetTBSpec)...
RetrySpec   EQU  $440E4A0E
AlterSpec   EQU  $360C3E0E
TWASpec     EQU  $00044F18
GotoLnSpec  EQU  $00020802
CursorSpec  EQU  $20022402
CrsLftSpec  EQU  $1E021E02
CrsRytSpec  EQU  $27022702
CursUpSpec  EQU  $1F021F02
CursDnSpec  EQU  $26022602
TextUpSpec  EQU  $29022902
TextDnSpec  EQU  $2E022E02
PageUpSpec  EQU  $30023002
PageDnSpec  EQU  $35023502
InsertSpec  EQU  $37024002
OvrflwSpec  EQU  $42024802
WrdProSpec  EQU  $4A024F02
Conterm     EQU  $484                       * See SetConterm.
GoCntrlCds  EQU  5                          * See ShowSpeshl.
GemDos      EQU  1                          * Constant used for TRAP #1 (i.e. TRAP #GemDos instead).
ConOut      EQU  2                          * These are all the GEMDOS function code constants. Used all over the place...
ConIn       EQU  8
PrntLine    EQU  9
DSetpath    EQU  $3B
DGetDrv     EQU  $19
FGetDTA     EQU  $2F
DFree       EQU  $36
FCreate     EQU  $3C
FOpen       EQU  $3D
FClose      EQU  $3E
FRead       EQU  $3F
FWrite      EQU  $40
SFirst      EQU  $4E
MShrink     EQU  $4A
Bios        EQU  13                         * Same purpose as GemDos constant (used as TRAP #Bios. e.g. see CheckPFNam).
BConstat    EQU  1                          * BIOS function constants...
BConin      EQU  2
Bconout     EQU  3
Drvmap      EQU  $0A
XBios       EQU  14                         * Ditto. i.e. used as TRAP #XBios. e.g. see CheckPFNam.
Vsync       EQU  37                         * XBIOS function call constants...
GetRes      EQU  4
SetScreen   EQU  5
SetPallete  EQU  6
SetColour   EQU  7


*      Must first free available memory ready for program initialisation...

Start:      MOVE.L 4(A7),A5                 * A5 now points to base page start exactly $100 bytes below the program start.
            MOVE.L $C(A5),D0                * Get length of program area.
            ADD.L $14(A5),D0                * Add on length of initialised data area.
            ADD.L $1C(A5),D0                * Add on length of uninitialised data area.
            ADD.L #$100,D0                  * Reserve $100 bytes for base page.
            MOVE.L D0,-(sp)                 * D0 contains the length of the area to be reserved.
            MOVE.L A5,-(sp)                 * A5 contains the start of the area to be reserved.
            CLR.W -(sp)                     * Meaningless but necessary.
            MOVE.W #MShrink,-(sp)           * Function number for GEMDOS Mshrink.
            TRAP #GemDos
            ADDA.W #12,sp                   * CS

*                               Memory now freed.

            JSR Initialise                  * Set up everything...
            JSR ScrnDsplay
            JSR Invert
            JSR Invert

Editor:     MOVE.B #Yes,UseMessage          * Initialise a few key variables and turn mouse & cursor on...
            MOVE.B #58,MessageXCo
            MOVE.B #2,MessageYCo
            MOVE.B MessageXCo,MainEdMXCo
            MOVE.B MessageYCo,MainEdMYCo
            MOVE.B #Yes,EdInsrtSt           * Entering mode is Insert at start.
            MOVE.B #Yes,InsrtState
            MOVE.B #Yes,LineChange          * New line at start.
            MOVE.B #Yes,RemNewXPos          * Set flag to reset remembered x position.
            MOVE.B #4,BuffStY
            MOVE.B #Yes,WrdProMode          * Word processor mode on.
            MOVE.B #No,SkipUndoSv
            MOVE.B #No,NewLnTaken
            MOVE.B #Yes,Overflow            * Character overflow allowed.
            JSR OffOvrflow                  * Do not confuse this statement and the one before!
            MOVE.B #-1,OldCrsXPos           * Initialise Old... variables:-
            MOVE.W #-1,OldNoLines
            MOVE.W #-1,OldLinePos
            CLR.B NewCrsrXCo
            MOVE.B #4,NewCrsrYCo
            JSR CrsrMvAc
            MOVE.B #Yes,MwsAcFlag

MainEdLoop: CLR.B ActionCode                * Clear action code.
            CLR.B KeybrdStat                * Clear status of special keys.
            MOVE.B #No,MwsFlag              * Clear mouse-cursor-movement flag.
            JSR GetCursPos                  * Save current cursor x-co. for cursor scrolling routines...
            CMPI.B #Yes,RemNewXPos          * Reset remembered x position?
            BNE ScrollScan                  * Branch if no.
            MOVE.B CursorXPos,ScrollXPos    * New position remembered.
            MOVE.B #No,RemNewXPos           * Clear flag.
ScrollScan: MOVE.B CursorYPos,YCo_ord       * Get current physical text line number...
            JSR FindLineNo
            MOVE.W D0,LineNumber
            JSR GetMsPos                    * Get info on mouse position, button states, e.t.c..
            JSR UpdateVars                  * Update the 3 variables shown on screen (Line, Column & Free).
            JSR CrsOnMwsOn                  * Turn on cursor and mouse if necessary.
            CMPI.B #No,LineChange           * Is the cursor now on a different line?
            BEQ NoLnChange                  * Branch if no.
            MOVE.B #No,LineChange           * Clear flag.
            JSR GetLineInf                  * Get info for current line...
            JSR SetCrntVrs
            CMPI.B #Yes,SkipUndoSv          * Is the saving of the current line to be ignored?
            BEQ SkipSvCrnt                  * Branch if yes.
            JSR SaveCurrnt                  * Otherwise, save current line to Undo buffer.
SkipSvCrnt: MOVE.B #No,SkipUndoSv           * Clear flag.
            CMPI.B #Yes,NewLnTaken          * Is the word wrap flag set?
            BNE NoLnChange                  * Branch if no.
            MOVE.W NwLnTknLn,D0             * Otherwise, is the cursor on the right line for word wrap to occur?
            CMP.W CurrntLine,D0
            BEQ NoLnChange                  * Branch if yes.
            MOVE.B #No,NewLnTaken           * Otherwise, clear flag.
NoLnChange: TST.B MwsChange                 * Has anything with the mouse changed/happened?
            BEQ NoMouseAc                   * Branch if no.
            JSR MouseOn                     * Otherwise, turn on mouse.
            MOVE.B #Yes,MwsAcFlag           * Set flag to show that the situation with the mouse has changed.
            TST.B ButChange                 * Has either button been pressed?
            BEQ NoMouseAc                   * Branch if no.
            MOVE.L #TWASpec,TxtBlkSpec      * Find out if mouse cursor is over the text window area (TWA)...
            JSR SetTBSpec                   * (set parameters for MwsOutside)
            JSR MwsOutside
            CMPI.B #No,MwsInside            * Is the mouse cursor within the TWA?
            BEQ MwsArwsTst                  * Branch if no.
            MOVE.B MwsChrYPos,YCo_ord       * Get line number that mouse is pointing at (legally) and accompanying info...
            JSR GetMsLnNo
            JSR GetNewXY                    
            JSR SetCrntVrs
            MOVE.W OldLinePos,D0            * Has the cursor moved to a different line?
            CMP.W CurrntLine,D0
            BEQ SameLineNo                  * Branch if no.
            JSR SaveCurrnt                  * Otherwise, take copy of line for Undo buffer and set ignore-undo-save flag...
            MOVE.B #Yes,SkipUndoSv
SameLineNo: TST.B LeftBut                   * Was it the left button that was pressed?
            BEQ GoRyteBut                   * Branch if no (i.e. if the right button was pressed).
JustCrsrMv: MOVE.B NewCrsrYCo,D0            * No need to move cursor if it's already there.
            CMP.B CursorYPos,D0             * Is the y-co different?
            BNE SetNewPosn                  * Branch if yes.
            MOVE.B NewCrsrXCo,D0            * But is the x-co different?
            CMP.B CursorXPos,D0
            BNE SkipLnChng                  * Branch if yes.
            BRA MainEdLoop                  * Otherwise, cursor postition is the same, so do nothing.
SetNewPosn: MOVE.B #Yes,LineChange          * Set flag.
SkipLnChng: DC.W HideMouse
            JSR CursrOff                    * Set cursor to new position and branch back for new scan...
            JSR CrsrMvAc
            MOVE.B #Yes,RemNewXPos          * Set flag to reset remembered x position.
            BRA MainEdLoop
GoRyteBut:  JSR RyteButAc                   * Set code to denote whether delete/insert a word is to happen.
            CMPI.B #Yes,MwsFlag             * Is the operation just a cursor movement?
            BEQ JustCrsrMv                  * Branch if yes.
            CMPI.B #UndoDWCode,ActionCode   * Is it an InsertWord action?
            BEQ InsWrd                      * Branch if yes.
            CMPI.B #DelWordCode,ActionCode  * Is it a DeleteWord action?
            BNE JustCrsrMv                  * Branch if no, as the line must be empty.
            DC.W HideMouse                  * Otherwise, get ready to delete a word...
            JSR CursrOff
            MOVE.B NewCrsrXCo,CursorXPos    * If cursor is at end of line, it must be moved left one first...
            MOVE.B CrntLnLen,D7
            CMP.B CursorXPos,D7             * Is this the case?
            BNE CursrOk                     * Branch if no.
            SUBQ.B #1,CursorXPos            * Otherwise, move cursor left one (memory only!).
CursrOk:    MOVE.B CursorXPos,Position      * Set start scan position for DeleteWord.
            MOVE.B CrntLnLen,BuffLength     * Set parameter for DeleteWord...
            JSR DeleteWord                  * ...and the word (if there is one) is gone.
            MOVE.B BuffLength,D0            * Correct cursor position if it's too far to the right...
            MOVE.B D0,CrntLnLen             * (set new line length)
            CMP.B NewCrsrXCo,D0
            BGE LineUpdate
            MOVE.B BuffLength,NewCrsrXCo
LineUpdate: JSR UpdateLn                    * Update line on screen.
            MOVE.W OldLinePos,D0            * Has the cursor moved to a different line?
            CMP.W CurrntLine,D0
            BNE SetNewPosn                  * Branch if yes.
            BRA SkipLnChng                  * Otherwise, branch to skip setting of LineChange flag.
InsWrd:     DC.W HideMouse
            JSR CursrOff
            JSR InsertWord                  * Old word inserted.
            BRA LineUpdate
MwsArwsTst: JSR SpshlKyScn
            MOVE.L #CursUpSpec,TxtBlkSpec   * Now test for the various mouse operated cursor/text control arrows...
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside            * Did the user click on cursor-up?
            BEQ NextMATst1                  * Branch if no.
            JSR Pause2
            BRA CursUpVec
NextMATst1: MOVE.L #CursDnSpec,TxtBlkSpec   * Did the user click on cursor-down?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NextMATst2                  * Branch if no.
            JSR Pause2
            BRA CursDnVec
NextMATst2: MOVE.L #CrsLftSpec,TxtBlkSpec   * Did the user click on cursor-left?
            JSR SetTBSpec
            JSR MwsOutside 
            CMPI.B #No,MwsInside
            BEQ NextMATst3                  * Branch if no.
            JSR Pause2
            BRA CursLftVec                  * Jump to identical code.
NextMATst3: MOVE.L #CrsRytSpec,TxtBlkSpec   * Did the user click on cursor-right?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NextMATst4                  * Branch if no.
            JSR Pause2
            BRA CursRytVec
NextMATst4: MOVE.L #TextUpSpec,TxtBlkSpec   * Did the user click on text-up?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NextMATst5                  * Branch if no.
            JSR TextUp
            BRA ScrollScan
NextMATst5: MOVE.L #TextDnSpec,TxtBlkSpec   * Did the user click on text-down?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NextMATst6                  * Branch if no.
            JSR TextDown
            BRA ScrollScan
NextMATst6: MOVE.L #PageUpSpec,TxtBlkSpec   * Did the user click on page-up?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NextMATst7                  * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well?
            BEQ JustPageUp                  * Branch if no.
            DC.W HideMouse
            JSR UpHalfPage
            BRA ScrollScan
JustPageUp: DC.W HideMouse
            JSR PageUp
            BRA ScrollScan
NextMATst7: MOVE.L #PageDnSpec,TxtBlkSpec   * Did the user click on page-down?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NextMATst8                  * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well?
            BEQ JustPageDn                  * Branch if no.
            DC.W HideMouse
            JSR DnHalfPage
            BRA ScrollScan
JustPageDn: DC.W HideMouse
            JSR PageDown
            BRA ScrollScan
NextMATst8: MOVE.L #CursorSpec,TxtBlkSpec   * Did the user click on the word 'Cursor'?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NextMATst9                  * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well?
            BEQ MATest8a                    * Branch if no.
            JSR SaveCrsPos                  * Otherwise, save the current position in the text file.
            JSR MouseOn
            BRA MainEdLoop
MATest8a:   BTST.B #ControlBit,KeybrdStat   * Has the Control key been pressed as well?
            BEQ MATest8b                    * Branch if no.
            JSR JumpToSavd                  * Otherwise, jump to saved position.
            JSR MouseOn
            BRA MainEdLoop
MATest8b:   TST.W SavedLine                 * Have the position save variables already been set to silly values?
            BMI MainEdLoop                  * Branch if yes.
            JSR FlashCursr                  * Otherwise, clear the position save variables...
            JSR SetToSilly
            JSR MouseOn
            BRA MainEdLoop
NextMATst9: MOVE.L #InsertSpec,TxtBlkSpec   * Did the user click on 'MD:.......'?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NxtMATst10                  * Branch if no.
            DC.W HideMouse
            JSR NoButton
            JSR FlipInsert                  * Otherwise, switch the entering mode...
            JSR MouseOn
            BRA MainEdLoop
NxtMATst10: MOVE.L #OvrflwSpec,TxtBlkSpec   * Did the user click on 'OFW:...'?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NxtMATst11                  * Branch if no.
            DC.W HideMouse
            JSR NoButton
            JSR FlipOvMode                  * Otherwise, switch the overflow flag.
            JSR MouseOn
            BRA MainEdLoop
NxtMATst11: MOVE.L #WrdProSpec,TxtBlkSpec   * Did the user click on 'WP:...'?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NxtMATst12                  * Branch if no.
            DC.W HideMouse
            JSR NoButton
            JSR FlipWPMode                  * Otherwise, switch the Word Processor flag.
            JSR MouseOn
            BRA MainEdLoop
NxtMATst12: MOVE.L #GotoLnSpec,TxtBlkSpec   * Did the user click on 'Line:xxxx'?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NxtMATst13                  * Branch if no.
            JSR GotoLine                    * Otherwise, call the goto-line routine.
            JSR MouseOn
            BRA MainEdLoop
NxtMATst13: MOVE.L #DeskSpec,TxtBlkSpec     * Did the user click on 'Desk'?
            JSR SetTBSpec
            JSR MwsOutside
            CMPI.B #No,MwsInside
            BEQ NxtMATst14                  * Branch if no.
            JSR DeskInfo
            BRA MainEdLoop
NxtMATst14: 
            BRA MainEdLoop
NoMouseAc:  JSR ScanKeybrd                  * Scan the keyboard.
            TST.B ScanCode                  * Has a key been pressed?
            BEQ MainEdLoop                  * Branch if no.
            JSR SpshlKyScn                  * Scan the special keys (Alternate, Shift, et.c.).
            BTST.B #AltBit,KeybrdStat       * Has the Alternate key been pressed?
            BEQ NoAltPresd                  * Branch if no (this avoids unnecessary key checks).
AltTest0:   CMPI.B #SaveKey,ScanCode        * Has the Save-file/Save-file-as key been pressed?
            BNE AltTest1                    * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Has Shift been pressed as well?
            BEQ SvFileAs                    * Branch if no. Save-file-as required.
            JSR SaveFile
            BRA MainEdLoop
SvFileAs:   JSR SaveFileAs
            BRA MainEdLoop
AltTest1:   CMPI.B #LoadKey,ScanCode        * Has the Load-file key been pressed?
            BNE AltTest2                    * Branch if no.
            JSR LoadFile
            BRA MainEdLoop
AltTest2:   CMPI.B #GotoLnKey,ScanCode      * Has the Goto-line key been pressed?
            BNE AltTest3                    * Branch if no.
            JSR GotoLine
            BRA MainEdLoop
AltTest3:   CMPI.B #QuitKey,ScanCode        * Has the Quit-program key been pressed?
            BNE AltTest4                    * Branch if no.
QuitVector: JSR QuitMainEd                  * Get user decision.
            CMPI.B #No,YNAnswer             * Does the user want to quit the program?
            BEQ MainEdLoop                  * Branch if no.
            BRA Quit                        * Otherwise, quit program.
AltTest4:   CMPI.B #CenterKey,ScanCode      * Has the Text-line-centering key been pressed?
            BNE AltTest5                    * Branch if no.
            JSR CenterLine
            BRA MainEdLoop
AltTest5:   CMPI.B #TpOfTxtKey,ScanCode     * Has the Jump-to-top-of-text key been pressed?
            BNE AltTest6                    * Branch if no.
            JSR ToTopOfTxt
            BRA MainEdLoop
AltTest6:   CMPI.B #BtOfTxtKey,ScanCode     * Has the Jump-to-top-of-text key been pressed?
            BNE AltTest7                    * Branch if no.
            JSR ToBotOfTxt
            BRA MainEdLoop
AltTest7:   CMPI.B #InvertKey,ScanCode      * Has the Invert-screen key been pressed?
            BNE AltTest8                    * Branch if no.
            JSR Invert
            BRA MainEdLoop
AltTest8:   CMPI.B #ClrFileKey,ScanCode     * Has the Clear-whole-of-text key been pressed?
            BNE AltTest9                    * Branch if no.
            MOVE.B KeybrdStat,-(sp)         * Save status of keyboard special keys.
            JSR ClearFile                   * Get user decision.
            MOVE.B (sp)+,KeybrdStat         * Restore keyboard status.
            CMPI.B #No,YNAnswer             * Does the user want to clear the text file/create a new text file?
            BEQ MainEdLoop                  * Branch if no.
            DC.W HideMouse                  * Initialise the various memory locations, variables, et.c., required & branch to
            JSR CursrOff                    * start of editor routine.
            JSR ClrTxtFile
            CLR.B UndoBufLen
            CLR.B WrdStrLen
            JSR ClrTxtWndw
            BTST.B #EthrShftBit,KeybrdStat  * Is the action a New-file one?
            BEQ AltTest8a                   * Branch if no.
            MOVE.L #FileName,SrcPtr         * Otherwise, clear the current file name...
            MOVE.B #FileBufLen,LineLen
            JSR ClearData
AltTest8a:  CLR.W TopLine                   * Reset the cursor position...
            JSR ShowText
            BRA Editor
AltTest9:   
NoAltPresd: CMPI.B #LnStartKey,ScanCode     * Has the Jump-to-start-of-line key been pressed?
            BNE KeyTests1                   * Branch if no.
            DC.W HideMouse
            TST.B CursorXPos                * Is cursor already at start of line?
            BEQ MainEdLoop                  * Branch if yes.
            CLR.B NewCrsrXCo                * Set new cursor position to be at start of line...
            MOVE.B CursorYPos,NewCrsrYCo    
            BRA SkipLnChng
KeyTests1:  CMPI.B #LineEndKey,ScanCode     * Has the Jump-to-end-of-line key been pressed?
            BNE KeyTests2                   * Branch if no.
            DC.W HideMouse
            MOVE.B CursorXPos,D0            * Is cursor already at end of line?
            CMP.B BuffLength,D0
            BEQ MainEdLoop                  * Branch if yes here...
            CMP.B MaxCrsrXCo,D0
            BEQ MainEdLoop                  * ...or here.            
            MOVE.B BuffLength,NewCrsrXCo    * Set new cursor position to be at start of line...
NewXPos:    MOVE.B CursorYPos,NewCrsrYCo    
            BRA SkipLnChng
KeyTests2:  CMPI.B #LftCrsrKey,ScanCode     * Has the left cursor key been pressed?
            BNE KeyTests3                   * Branch if no.
CursLftVec: DC.W HideMouse
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well?
            BEQ KeyTests2a                  * Branch if no.
            JSR IsLftArrOk                  * Check to see if action is ok.
            CMPI.B #No,YNAnswer             * Is action allowed?
            BEQ MainEdLoop                  * Branch if no.
            JSR CursrOff
            JSR WordLeft                    * Move the cursor to new position one word leftwards...
            BRA NewXPos
KeyTests2a: BTST.B #ControlBit,KeybrdStat   * Has Control been pressed as well?
            BEQ KeyTests2b                  * Branch if no.
            TST.B CursorXPos                * Is cursor already at start of line?
            BEQ MainEdLoop                  * Branch if yes.
            CLR.B NewCrsrXCo                * Set new cursor position to be at start of line...
            BRA NewXPos
KeyTests2b: JSR CursorLeft
            BRA MainEdLoop
KeyTests3:  CMPI.B #RtCrsrKey,ScanCode      * Has the right cursor key been pressed?
            BNE KeyTests4                   * Branch if no.
CursRytVec: DC.W HideMouse
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well?
            BEQ KeyTests3a                  * Branch if no.
            JSR IsRyteArOk                  * Check to see if action is ok.
            CMPI.B #No,YNAnswer             * Is action allowed?
            BEQ MainEdLoop                  * Branch if no.
            JSR CursrOff
            JSR WordRight                   * Move the cursor to new position one word rightwards...
            BRA NewXPos
KeyTests3a: BTST.B #ControlBit,KeybrdStat   * Has control been pressed as well?
            BEQ KeyTests3b                  * Branch if no.
            MOVE.B CursorXPos,D0            * Is cursor already at end of line?
            CMP.B BuffLength,D0
            BEQ MainEdLoop                  * Branch if yes here...
            CMP.B MaxCrsrXCo,D0
            BEQ MainEdLoop                  * ...or here.            
            MOVE.B BuffLength,NewCrsrXCo    * Set new cursor position to be at start of line...
            BRA NewXPos
KeyTests3b: JSR CursorRyte
            BRA MainEdLoop
KeyTests4:  CMPI.B #UpCrsrKey,ScanCode      * Has the up cursor key been pressed?
            BNE KeyTests5                   * Branch if no.
CursUpVec:  DC.W HideMouse
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well?
            BEQ KeyTests4a                  * Branch if no.
            BTST.B #ControlBit,KeybrdStat   * Has Control been pressed as well?
            BEQ JustDnHfPg                  * Branch if no.
            JSR TextDown
            BRA ScrollScan
JustDnHfPg: JSR DnHalfPage
            BRA ScrollScan
KeyTests4a: BTST.B #ControlBit,KeybrdStat   * Has Control been pressed as well?
            BEQ KeyTests4b                  * Branch if no.
            JSR PageDown
            BRA ScrollScan
KeyTests4b: JSR CursorUp
            BRA ScrollScan
KeyTests5:  CMPI.B #DnCrsrKey,ScanCode      * Has the down cursor key been pressed?
            BNE KeyTests6                   * Branch if no.
CursDnVec:  DC.W HideMouse
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well?
            BEQ KeyTests5a                  * Branch if no.
            BTST.B #ControlBit,KeybrdStat   * Has Control been pressed as well?
            BEQ JustUpHfPg                  * Branch if no.
            JSR TextUp
            BRA ScrollScan
JustUpHfPg: JSR UpHalfPage
            BRA ScrollScan
KeyTests5a: BTST.B #ControlBit,KeybrdStat   * Has Control been pressed as well?
            BEQ KeyTests5b                  * Branch if no.
            JSR PageUp
            BRA ScrollScan
KeyTests5b: JSR CursorDown
            BRA ScrollScan
KeyTests6:  CMPI.B #DeleteKey,ScanCode      * Has the Delete key been pressed?
            BNE KeyTests7                   * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Was Shift pressed as well?
            BNE KeyTests6a                  * Branch if yes.
            JSR Delete
            BRA MainEdLoop
KeyTests6a: JSR ShftDelete
            BRA MainEdLoop
KeyTests7:  CMPI.B #BackspcKey,ScanCode     * Has the Backspace key been pressed?
            BNE KeyTests8                   * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Was Shift pressed as well?
            BNE KeyTests7a                  * Branch if yes.
            JSR Backspace
            BRA MainEdLoop
KeyTests7a: JSR ShBackspc
            BRA MainEdLoop
KeyTests8:  CMPI.B #ReturnKey,ScanCode      * Has the Return key been pressed?
            BNE KeyTests9                   * Branch if no.
            JSR Return
            BRA MainEdLoop
KeyTests9:  CMPI.B #InsertKey,ScanCode      * Has the Insert key been pressed?
            BNE KeyTsts10                   * Branch if no.
            DC.W HideMouse                  * Otherwise, switch the entering mode...
            MOVE.B EdInsrtSt,InsrtState
            JSR FlipInsert
            MOVE.B InsrtState,EdInsrtSt
            BRA MainEdLoop
KeyTsts10:  CMPI.B #ClrHomeKey,ScanCode     * Has the move-to-top-left-of-screen key been pressed?
            BNE KeyTsts11                   * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Has Shift been pressed as well?
            BNE KeyTsts10a                  * Branch if yes.
            JSR ToTopLeft                   * Otherwise, jump to top left.
            BRA MainEdLoop
KeyTsts10a: JSR ToBotmLeft
            BRA MainEdLoop
KeyTsts11:  CMPI.B #UndoKey,ScanCode        * Has the restore-line key been pressed?
            BNE KeyTsts12                   * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Has Shift been pressed as well?
            BEQ NoUndoShft                  * Branch if no.
            JSR SaveCurrnt                  * Otherwise, set the Undo buffer to the current text line.
            JSR FlashScrn
            BRA MainEdLoop
NoUndoShft: JSR RestorLine
            BRA MainEdLoop
KeyTsts12:  CMPI.B #HelpKey,ScanCode        * Has the Help key been pressed?
            BNE KeyTsts13                   * Branch if no.
            JSR Help
            BRA MainEdLoop
KeyTsts13:  CMPI.B #ToMidLftKy,ScanCode     * Has the jump-to-middle-left-of-screen key been pressed? (Control + Clr Home)
            BNE KeyTsts14                   * Branch if no.
            JSR ToMidLeft                   * Otherwise, move cursor to middle left of screen.
            BRA MainEdLoop
KeyTsts14:  NOP
CtrlTsts1:  BTST.B #ControlBit,KeybrdStat   * Has the Control key been pressed?
            BEQ NoCtrlKey                   * Branch if no (this avoids unnecessary key checks).
            CMPI.B #KillLnKey,ScanCode      * Has the Kill-line key been pressed?
            BNE CtrlTsts2                   * Branch if no.
            JSR KillLine
            BRA MainEdLoop
CtrlTsts2:  CMPI.B #DelLnKey,ScanCode       * Has the Totally-delete-the-line key been pressed?
            BNE CtrlTsts3                   * Branch if no.
            BTST.B #EthrShftBit,KeybrdStat  * Has either Shift key been pressed as well (to undo a previously killed line)?
            BEQ JustDelLn                   * Branch if no (operation is just Delete the line).
            JSR UndoDelLn                   * Otherwise, replace the line that was previously deleted.
            BRA MainEdLoop
JustDelLn:  JSR DeleteLine                  * Delete the current line.
            BRA MainEdLoop
CtrlTsts3:  CMPI.B #PosRemKey,ScanCode      * Has the Remember-current-cursor-position key been pressed?
            BNE CtrlTsts4                   * Branch if no.
            JSR SaveCrsPos                  * Otherwise, save the current position in the text file.
            BRA MainEdLoop
CtrlTsts4:  CMPI.B #JmpToRmKey,ScanCode     * Has the Jump-to-remembered-cursor-position key been pressed?
            BNE CtrlTsts5                   * Branch if no.
            JSR JumpToSavd                  * Otherwise, jump to saved position.
            BRA MainEdLoop
CtrlTsts5:  CMPI.B #ClearRmKey,ScanCode     * Has the Clear-cursor-position-memory key been pressed?
            BNE CtrlTsts6                   * Branch if no.
            TST.B SavedXPos                 * But, have the position-remember variables already been cleared?
            BMI MainEdLoop                  * Branch if yes.
            JSR FlashCursr                  * Otherwise, clear the position save variables...
            JSR SetToSilly
            BRA MainEdLoop
CtrlTsts6:  CMPI.B #OverflwKey,ScanCode     * Has the Overflow-On/Off key been pressed?
            BNE CtrlTsts7                   * Branch if no.
            DC.W HideMouse                  * Otherwise, switch the Overflow flag...
            JSR FlipOvMode
            BRA MainEdLoop
CtrlTsts7:  CMPI.B #WordProKey,ScanCode     * Has the Word-Processor-On/Off key been pressed?
            BNE CtrlTsts8                   * Branch if no.
            DC.W HideMouse                  * Otherwise, switch the Word-Processor flag...
            JSR FlipWPMode
            BRA MainEdLoop
CtrlTsts8:  CMPI.B #ContrlCKey,ScanCode     * It is common for many programs to use Control C for 'Quit Program':
            BEQ QuitVector                  * Has Control + C been pressed? Branch if yes.
CtrlTsts9:  
NoCtrlKey:  CMPI.B #32,NextChar             * Is there a valid ASCII code present?
            BLT MainEdLoop                  * Branch if no (this prevents control codes).
            JSR Character                   * Otherwise, place the character into the text file and branch back...
            BRA MainEdLoop

Quit:       MOVE.L #PalleteBuf,-(sp)        * Must reset colour pallete to original status before quitting.
            MOVE.W #SetPallete,-(sp)        * Code for XBios SetPallete.
            TRAP #XBios
            ADDQ.L #6,sp                    * CS
            CMPI.W #2,OrignalRes            * Are we in high res. mode?
            BEQ ByeBye                      * Branch if yes.
            MOVE.W OrignalRes,-(sp)         * Otherwise, must reset to original resolution...
            MOVE.L #-1,-(sp)                * (same screen physical base)
            MOVE.L #-1,-(sp)                * (same screen logical base)
            MOVE.W #SetScreen,-(sp)         * (code for XBios SetScreen)
            TRAP #XBios
            ADD.L #12,sp                    * CS
ByeBye:     CLR.W -(sp)                     * Time to go...
            TRAP #GemDos                    * Bye!
            RTS




* SUBROUTINE Initialise. This routine sets up the mouse, screen, TFM, character look up table and the values of certain variables.

Initialise: JSR SetResoltn
            JSR ChangeMows
            JSR SetConterm
            JSR Init
            JSR OnOverflow
            JSR SetCharTbl
            JSR InitFile
            RTS


* SUBROUTINE SetResoltn. This routine ensures that the resolution is correct for program operation.
*                        Algorithm: If resolution < 2 THEN resolution = 1,
*                        where 0 = Low res., 1 = Medium res., 2 = High res..

SetResoltn: MOVEM.L D5-D6/A5-A6,-(sp)       * SRTBU
            MOVE.W #GetRes,-(sp)            * First obtain the current resolution...
            TRAP #XBios
            ADDQ.L #2,sp                    * CS
            MOVE.W D0,OrignalRes            * Save initial resolution (this will be needed when the program quits).
            MOVE.W D0,CurrentRes
            MOVE.W #2,D6                    * D6 used to obtain pallete colours further down. Assume 2 col's for high res.
            CMPI.L #2,D0                    * Are we in high res?
            BEQ SkipResSet                  * Branch if yes.
            MOVE.W #16,D6                   * Change to 16 col's for medium res.
            MOVE.W #1,CurrentRes            * Otherwise, set to medium resolution...
            MOVE.W #1,-(sp)
            MOVE.L #-1,-(sp)                * Retain physical screen base.
            MOVE.L #-1,-(sp)                * Retain logical screen base.
            MOVE.W #SetScreen,-(sp)
            TRAP #XBios
            ADD.L #12,sp                    * CS
SkipResSet: MOVEA.L #PalleteBuf,A5          * Obtain original pallete setup and create new inverted pallete...
            MOVEA.L #NewPallete,A6
            CLR.W D5                        * Counter used to get colours.
StoreColrs: MOVE.W #-1,-(sp)                * Get current colour for colour number in D5...
            MOVE.W D5,-(sp)
            MOVE.W #SetColour,-(sp)         * Code for XBIOS SetColour (=7).
            TRAP #XBios                     * Call XBIOS.
            ADDQ.L #6,sp                    * CS
            MOVE.W D0,(A5)+                 * Store this original colour in the pallete buffer.
            EOR.W #$777,D0                  * Now invert the colour value and store in new pallete buffer...
            MOVE.W D0,(A6)+
            ADDQ.W #1,D5                    * Get value of next colour number.
            CMP.W D5,D6                     * Finished yet?
            BNE StoreColrs                  * Branch if no.
            MOVE.L #NewPallete,-(sp)        * Now set new pallete to inverted pallete...
            MOVE.W #SetPallete,-(sp)        * Code for XBios SetPallete.
            TRAP #XBios
            ADDQ.L #6,sp                    * CS
            MOVEM.L (sp)+,D5-D6/A5-A6       * RRU
            RTS


* SUBROUTINE ChangeMows. This routine changes the mouse's appearance to a suitable cross sight (ha ha).

ChangeMows: DC.W HideMouse                  * Mouse off.
            DC.W InitA                      * Get line A variable addresses.
            MOVE.L #Mouse,IntIn(A0)         * Set address of mouse data...
            DC.W TrnsMouse                  * ...and set form.
            RTS


* SUBROUTINE SetConterm. This routine sets bit 3 of the system variable Conterm (at $484) so that the result of the Bios call
*                        Bconin also returns the result of the Bios function Kbshift in bits 24 to 31. This allows direct
*                        checks to made on certain key combinations (e.g. ALT S to save a file).
*                        The routine has to enter Supervisor mode to do this, but only temporarily.
*                        This routine also returns the location of the special keys bit status vector in KbshiftLoc.

SetConterm: CLR.L -(sp)                     * The user stack becomes the Supervisor stack.
            MOVE.W #$20,-(sp)               * Code for calling SUPER function onto stack.
            TRAP #GemDos                    * Supervisor mode is active after the TRAP call.
            ADDQ.W #6,sp                    * CS
            MOVE.L D0,SaveSSP               * D0 = old supervisor stack, so save!!!
            BSET.B #3,Conterm               * Set bit 3 of the system variable Conterm.
            MOVE.L A4,-(sp)                 * SRTBU
            MOVEA.L $4F2,A4                 * Get location of rom.
            CMP.W #$0100,2(A4)              * Is the rom a new rom version?
            BNE NewRoms                     * Branch if yes.
            MOVE.L #$E1B,KbshiftLoc         * Transfer address of Kbshift status byte.
            BRA SCContinue
NewRoms:    MOVE.L $24(A4),KbshiftLoc       * Transfer address of Kbshift status byte.
SCContinue: MOVE.L (sp)+,A4                 * RRU
            MOVE.L SaveSSP,-(sp)            * Old supervisor stack pointer on stack.
            MOVE.W #$20,-(sp)               * Code for calling SUPER function onto stack.
            TRAP #GemDos                    * Back in User mode after the TRAP instruction.
            ADDQ.W #6,sp                    * CS
            RTS


* SUBROUTINE SetCharTbl. This routine sets up the character data look up table for the displaying of characters directly to the
*                        screen ram. It is done by printing out the entire character set to the screen and then copying the data to
*                        a 4K buffer that has been set up via Malloc. This routine will work in any mode.

SetCharTbl: MOVEM.L D0/D5/A5-A6,-(sp)       * SRTBU
            MOVE.L #$1000,-(sp)             * Need 4K for the character look up table (16 bytes each for 256 characters).
            MOVE.W #$48,-(sp)               * Call Malloc to reserve that amount of RAM.
            TRAP #GemDos
            ADDQ.W #6,sp                    * CS
            MOVE.L D0,CharTblPtr            * CharTblPtr now contains the start address of the reserved memory.
            CLR.L -(sp)                     * The user stack becomes the Supervisor stack.
            MOVE.W #$20,-(sp)               * Code for calling SUPER function onto stack.
            TRAP #GemDos                    * Supervisor mode is active after the TRAP call.
            ADDQ.W #6,sp                    * CS
            MOVE.L $44E,ScreenStrt          * Save pointer to start of screen RAM.
            MOVE.L D0,-(sp)                 * Old supervisor stack pointer on stack.
            MOVE.W #$20,-(sp)               * Code for calling SUPER function onto stack.
            TRAP #GemDos                    * Back in User mode after the TRAP instruction.
            ADDQ.W #6,sp                    * CS
            DC.W HideMouse                  * Make sure mouse and cursor are off, then clear the screen...
            JSR CursrOff
            JSR WipeScrn
            CLR.B SrcChar                   * First character to print is ASCII 0. SrcChar will also be used as the loop counter.
            MOVEA.L CharTblPtr,A6           * Each character is displayed at position 0,0 on screen. The ram locations for that
SCTLoop1:   CLR.B Column                    * position are then copied into the next 16 bytes of the look up table. So, first
            CLR.B Line                      * display the enxt character at text co-ords 0,0...
            JSR MoveCurs
            JSR ShowSpeshl
            MOVEA.L ScreenStrt,A5           * Next copy the ram locations affected into the next 16 bytes of the look up table...
            MOVE.W #15,D5                   * (D5 used as counter. Set to 1 less for DBRA)
SCTLoop2:   MOVE.B (A5),(A6)+               * Copy next byte.
            ADDA.L #80,A5                   * Next byte to copy is, on the screen, directly below the one just copied. Offset = 80.
            DBRA D5,SCTLoop2                * Branch back if not finished.
            ADDQ.B #1,SrcChar               * Increment character code and counter.
            BNE SCTLoop1                    * Branch back if not finished.
            MOVEM.L (sp)+,D0/D5/A5-A6       * RRU
            RTS


* SUBROUTINE Init.  This initialises all the necessary variables and memory locations before the main program loop gets going.

Init:       MOVE.B #80,LineLen              * Initialise DelLnBuf...
            MOVE.L #DelLnBuf,SrcPtr
            JSR ClearData
            CLR.B DLBfLength
            MOVE.B #81,LineLen              * Clear all text buffers. Must also place a zero byte at the end of all the various
            MOVE.L #TempBuff,SrcPtr         * text buffers to signify termination when the buffers are totally full,
            JSR ClearData                   * so LineLen = MaxLineLen + 1 = 81
            MOVE.L #WordStore,SrcPtr
            JSR ClearData
            MOVE.L #LineWork,SrcPtr
            JSR ClearData
            MOVE.L #OldPFName,SrcPtr
            JSR ClearData
            MOVE.L #PthFileNam,SrcPtr
            JSR ClearData
            MOVE.L #GotoLineBf,SrcPtr       * Initialise the Goto Line number buffer...
            MOVE.B #6,LineLen
            JSR ClearData
            MOVE.B #'1',GotoLineBf
            MOVE.B #27,MvCursor             * Initialise the cursor movement buffer (insert codes for ESC 'Y')...
            MOVE.B #'Y',MvCursor+1
            CLR.B Column+1                  * Must insert zero byte at end of cursor movement command string.
            MOVE.W #DGetDrv,-(sp)           * Initialise CurrentDrv to the actual current drive...
            TRAP #GemDos                    * ...by calling GEMDOS function DGetDrv which returns the current drive number in D0.
            ADDQ.W #2,sp                    * CS
            MOVE.B D0,CurrentDrv            * N.B. As CurrentDrv is supposed to contain a letter, add the ASCII code for 'A' onto
            ADDI.B #'A',CurrentDrv          * the drive number that DGetDrv returned.
            MOVE.L #PathName,SrcPtr         * Must now initialise path and file names. Using buffer length of each  + 1 inserts
            MOVE.B #PathBufLen,LineLen      * the necessary zero byte at the end of each buffer.
            JSR ClearData
            MOVE.B CurrentDrv,PathName      * Insert current drive specification into path name (always <drv>:\)...
            MOVE.B #':',PathName+1
            MOVE.B #'\',PathName+2
            MOVE.L #FileName,SrcPtr         * Now clear file name...
            MOVE.B #FileBufLen,LineLen
            JSR ClearData
            LEA ErrCodeLst,A1               * Set up error vector table for Tos error messages...
            LEA ErrDstList,A2
            MOVE.L NumErrMsgs,D1
            SUBQ.B #1,D1                    * (subtract 1 since loop is using DBRA)
            CLR.W D2
            CLR.W D3
SetCodeLst: MOVE.B (A2)+,D3
            LSL.W #1,D3
            MOVE.W D2,0(A1,D3.W)
            ADDQ.B #4,D2
            DBRA D1,SetCodeLst
            CLR.B WrdStrLen                 * Nothing in word store at start, so set length of word store contents to zero.
            MOVE.B #79,MaxCrsrXCo           * Default maximum x cursor setting.
            MOVE.B #80,MaxLineLen           * Set maximum number of characters allowed in a text line.
            MOVE.W #-1,-(sp)                * Set cursor to steady (non flashing). First paramater not to be changed.
            MOVE.W #3,-(sp)                 * Function steady cursor.
            MOVE.W #21,-(sp)                * XBIOS function Cursconf.
            TRAP #XBios                     * Call XBIOS.
            ADDQ.L #6,sp                    * CS
            JSR GetDskTrAd                  * Get current disk transfer address for file accessing.
            MOVE.B #No,TempBuffSt           * Set flag for temporary buffer (initially empty).
            MOVE.B #Yes,Overflow            * By default, when user is typing, a new line is taken when the current line is full.
            JSR SetToSilly                  * Initialise position save variables.
            MOVE.W #1,NumLines              * Initialise the number of lines in the text file.
            RTS


* SUBROUTINE InitFile. This routine sets up the text file. FileStart is set to point to the start of the file. MaxNoLines is set
*                      to the maximum allowed length of the file (in lines of 80 column text). The text file memory is cleared.
*                      Note there is no EQUate for GEMDOS Malloc to prevent accidental use elsewhere.

InitFile:   MOVE.L #-1,-(sp)                * Must determine number of available bytes of free memory by calling GEMDOS Malloc
            MOVE.W #$48,-(sp)               * with parameter -1 (return free memory data). No. of available bytes returned in D0.
            TRAP #GemDos                    * (N.B. Code for Malloc is $48)
            ADDQ.W #6,sp                    * CS. Must now determine length of file (in lines) from D0. This is based on single page
            MOVE.L D0,D1                    * blocks of A4, each page consisting of 70 lines of 80 column text.
            LSR.L #3,D1                     * Final result will be an exact multiple of 70.
            DIVU.W #10,D1
            LSR.L #8,D0                     * Calculation equivalent to: D1 = (((D0 DIV 80) - (D0 DIV 4096))) DIV 70) * 70
            LSR.L #4,D0                     *                       This part  ^...........................^ is roughly the same
            SUB.W D0,D1                     *                       as dividing by 81 (cannot be done by DIVU).
            ANDI.L #$FFFF,D1                * (mask out high word as it is not wanted)
            DIVU.W #70,D1                   * Get no. of A4 pages.
            MULU.W #70,D1                   * Get final number of lines.
            MOVE.W D1,MaxNoLines            * Maximum number of lines in file now set.
            MULU.W #81,D1                   * Calculate number of bytes needed for the number of lines.
            MOVE.L D1,-(sp)                 * D1 now contains the required number of bytes for the text file, so call Malloc ($48)
            MOVE.W #$48,-(sp)               * reserving this number of bytes.
            TRAP #GemDos
            ADDQ.W #6,sp                    * CS
            MOVE.L D0,FileStart             * FileStart now points to start of first line of text file.
            JSR ClrTxtFile                  * Clear the whole of the text file area.
            RTS


* SUBROUTINE ClrTxtFile. This routine totally clears the whole of the text file area.
*            The file structure consists of a non-zero integer number of A4 pages. A standard A4 page has 70 lines.
*            Note, therefore, that when the program file is empty, the number of free lines shown plus one will be an exact
*            multiple of 70). The memory is cleared by repeated CLR.L operations. However, if the number of A4 pages available is
*            odd, the no. of long words to clear will be an integer + half, and if the no. of pages is even, then the no. of
*            long words will be an integer. Therefore, if the former is true, first clear the extra word, then clear the rest.


ClrTxtFile: MOVEM.L D4-D6/A5,-(sp)          * SRTBU
            MOVEA.L FileStart,A5            * A5 points to start of text file.
            MOVE.W MaxNoLines,D4            * Calculate no. of bytes to clear..
            MOVE.W D4,D5                    * (take copy of no. lines for use later)
            MULU.W #81,D4
            ADDA.L D4,A5                    * A5 now points to last byte of text file + 1.
            LSR.L #2,D4                     * D4 now contains the number of long words to clear, BUT...
            SUBQ.L #1,D4                    * set to one less as DBRA is used in the clearing loops. Need two loops as DBRA can
            SWAP D4                         * only decrement a register's lower word (bits 0->15). So...
            MOVE.W D4,D6                    * ...set D6 to outer loop value.
            CLR.W D4
            SWAP D4                         * D4 ready again.
            DIVU.W #140,D5                  * If number of A4 pages to clear is odd then number of words to clear is odd.
            SWAP D5                         * If so, must clear last word in the TFM to leave integer no. of long words to clear.
            TST.W D5                        * Was the no. of pages odd?
            BEQ CTFCont                     * Branch if no. Integer no. of long words to clear.
            CLR.W -(A5)                     * Otherwise, clear the extra word. No need to change D4 as fraction not included.
CTFCont:    CLR.L -(A5)                     * Clear next long word.
            DBRA D4,CTFCont                 * Branch back if not finished.
            DBRA D6,CTFCont                 * (ditto)
            MOVEM.L (sp)+,D4-D6/A5          * RRU
            MOVE.W #1,NumLines
            RTS

            
* SUBROUTINE Invert.   This routine inverts the screen display once.

Invert:     MOVEM.L D6/A5,-(sp)             * SRTBU
            MOVEA.L #NewPallete,A5          * Set pointer to NewPallete buffer.
            MOVE.W #1,D6                    * Assume 2 colours to be inverted (for high res.), so set to one less for DBRA.
            CMPI.W #2,CurrentRes            * Are we in high res.?
            BEQ SetColLoop                  * Branch if yes.
            MOVE.W #15,D6                   * Otherwise, we must be in medium res., so set noumber of colours to change to 16.
SetColLoop: EOR.W #$777,(A5)+               * Invert next colour in NewPallete buffer.
            DBRA D6,SetColLoop              * Branch back if not finished.
            MOVE.L #NewPallete,-(sp)        * Now change pallete to new set of colours.
            MOVE.W #SetPallete,-(sp)        * Code for XBios SetPallete.
            TRAP #XBios
            ADDQ.L #6,sp                    * CS
            MOVEM.L (sp)+,D6/A5             * RRU
            RTS


* SUBROUTINE ScrnDsplay. This routine sets up the screen display for the main editor.

ScrnDsplay: JSR WipeScrn                    * Clear any old garbage.
            DC.W InitA                      * Get address of line A opcode variables in A0. Set parameters for text/info bar...
            CLR.W 38(A0)                    * X co-ord upper left.
            MOVE.W #399,44(A0)              * X co-ord lower right.
            MOVE.L #53,D5                   * (upper left y-co for high res.)
            MOVE.L #58,D6                   * (lower right y-co for high res.)
            CMPI.W #2,CurrentRes            * Are we in high res. mode?
            BEQ BarYCosOk                   * Branch if yes as the y co-ordinates for the text/info bar are o.k.
            LSR.L #1,D5                     * Otherwise, halve the two y co-ord's...
            LSR.L #1,D6
BarYCosOk:  MOVE.W D5,40(A0)                * Now store correct y co-ords...
            MOVE.W D6,44(A0)
            MOVE.W #1,24(A0)                * Set bit plane 0 colour value,
            CLR.W 26(A0)                    * Clear other unwanted bit plane values...
            CLR.W 28(A0)
            CLR.W 30(A0)
            CLR.W 36(A0)                    * Set write mode to 0 = replace background,
            MOVE.L #LineFill,46(A0)         * Set fill pattern pointer,
            CLR.W 50(A0)                    * Clear pattern mask,
            CLR.W 54(A0)                    * Clear clipping flag,
            DC.W FilledRect                 * Draw text partition bar.
            MOVE.L #ScrnDsply1,SrcPtr       * Show 1st line of screen display...
            JSR PrntStr
            MOVE.L #ScrnDsply2,SrcPtr       * Show 2nd line of screen display...
            JSR PrntStr
            MOVE.L #ScrnDsply3,SrcPtr       * Show 3rd line of screen display...
            JSR PrntStr
            MOVE.B #2,D5                    * There are 3 sets of up & down arrows to be shown (all on line 2), so use 2 for DBRA.
            LEA UpArrwList,A3               * Get address of up arrow x co-ordinate list.
            LEA DnArrwList,A4               * Get address of down arrow x co-ordinate list.
            MOVE.B #2,NewCrsrYCo            * All arrows to be shown on line 2.
            JSR ReverseOn                   * All arrows are shown in inverse to show they are mouse operated.
ArrowLoop:  MOVE.B 0(A3,D5),NewCrsrXCo      * Move cursor to where next up arrow character is to be shown...
            JSR CrsrMvAc
            MOVE.B #UpArrowChr,SrcChar      * Display up arrow...
            JSR ShowSpeshl
            MOVE.B 0(A4,D5),NewCrsrXCo      * Move cursor to where next down arrow character is to be shown...
            JSR CrsrMvAc
            MOVE.B #DnArrowChr,SrcChar      * Display down arrow...
            JSR ShowSpeshl
            DBRA D5,ArrowLoop               * If not finished, branch back.
            JSR ReverseOff
            MOVE.L #LArrow,SrcPtr           * Show the left arrow and right arrow on line 2...
            JSR PrntStr                     * (move cursor for left arrow)
            MOVE.B #4,SrcChar               * (4 is the extended ASCII code for a left arrow)
            JSR ShowSpeshl
            MOVE.L #RArrow,SrcPtr           * (move cursor for right arrow)
            JSR PrntStr
            MOVE.B #3,SrcChar               * (3 is the extended ASCII code for a right arrow)
            JSR ShowSpeshl
            JSR ReverseOff
            RTS


* SUBROUTINE UpdateVars. This routine updates (if necessary) the 3 variables shown on line 2 of the screen display.

UpdateVars: MOVE.B #No,ScrnChange           * Assume nothing will change.
            MOVE.B CursorXPos,D0            * Has the screen cursor x-co position changed?
            CMP.B OldCrsXPos,D0
            BNE UpdateXPos                  * Branch if yes.
TestOldLn:  MOVE.W CurrntLine,D0            * Has the screen cursor line position changed?
            CMP.W OldLinePos,D0
            BNE UpdtLine                    * Branch if yes.
TestFree:   MOVE.W NumLines,D0              * Has the number of lines in the TFM changed?
            CMP.W OldNoLines,D0
            BNE UpdateFree                  * Branch if yes.
TestChange: CMPI.B #Yes,ScrnChange          * Was anything changed?
            BEQ ReposnCrsr                  * Branch if yes to reposition cursor.
            RTS
ReposnCrsr: MOVE.B CursorXPos,Column        * Otherwise, move cursor back to original position...
            MOVE.B CursorYPos,Line
            JSR MoveCurs
            RTS
UpdateXPos: MOVE.B D0,OldCrsXPos            * Save new position.
            ANDI.L #$FF,D0                  * Mask out any garbage.
            ADDQ.B #1,D0                    * Cursor co-ords start at 'column 0'; adjust so is shown as 'column 1,2,...'.
            MOVE.L D0,Value                 * Convert to ASCII string...
            JSR Convert
            DC.W HideMouse
            JSR CursrOff
            MOVE.B #2,FieldWidth            * Display the new value...
            MOVE.B #2,Line
            MOVE.B #14,Column
            JSR MoveCurs
            JSR PrintNum
            MOVE.B #Yes,ScrnChange          * Set flag as something has changed.
            BRA TestOldLn
UpdtLine:   MOVE.W D0,OldLinePos            * Save new position.
            ANDI.L #$FFFF,D0                * Mask out any garbage.
            MOVE.L D0,Value                 * Convert to ASCII string...
            JSR Convert
            DC.W HideMouse
            JSR CursrOff
            MOVE.B #4,FieldWidth            * Display the new value...
            MOVE.B #2,Line
            MOVE.B #5,Column
            JSR MoveCurs
            JSR PrintNum
            MOVE.B #Yes,ScrnChange          * Set flag as something has changed.
            BRA TestFree
UpdateFree: MOVE.W D0,OldNoLines            * Save new position.
            MOVE.W MaxNoLines,D1            * Calculate no. of free lines...
            SUB.W D0,D1
            BNE SomeLeft                    * Branch if there are still some lines left to use.
            MOVE.L #NoneLeft,SrcPtr         * Otherwise, show none left and branch...
            JSR PrntStr
            BRA FreeShown
SomeLeft:   ANDI.L #$FFFF,D1                * Mask out any garbage.
            MOVE.L D1,Value                 * Convert to ASCII string...
            JSR Convert
            DC.W HideMouse
            JSR CursrOff
            MOVE.B #2,Line                  * Display the new value...
            MOVE.B #22,Column
            JSR MoveCurs
            MOVE.B #4,FieldWidth
            JSR PrintNum
FreeShown:  MOVE.B #Yes,ScrnChange          * Set flag as something has changed.
            BRA TestChange


* SUBROUTINE GetNewXY. This routine returns the effective cursor position (after any necessary corrections) that the mouse cursor
*                      is pointing at.
*                      LineNumber must contain the physical line that the mouse is pointing at.

GetNewXY:   JSR GetLineYCo
            MOVE.B D0,NewCrsrYCo            * Save this position.
            JSR GetLineInf
            MOVE.B MwsChrXPos,NewCrsrXCo    * If equivalent cursor x position is past end of chosen line, set to end of line...
            JSR GetOkXCo
            RTS


* FUNCTION GetLineInf. This routine returns various information for a given text line and the current cursor position.
*                      LineNumber must contain the physical line number of line for which info is required.
*                      TextBuff returns the start address of the given text line.
*                      BuffLength returns the number of characters in that line.
*                      MaxAddress returns the value of TextBuff + 80. CopyData2 needs this to prevent overflow.
*                      D0 returns Yes if the line is visible on the screen, else D0 returns No.

GetLineInf: MOVE.W LineNumber,D0            * Set TextBuff...
            JSR GetLineAdr
            MOVE.L D0,TextBuff
            MOVE.L D0,SrcPtr                * Set BuffLength...
            JSR FindBufLen
            CLR.L D0                        * Set MaxAddress (see CopyData2 for use of this variable).
            MOVE.B MaxLineLen,D0
            ADD.L TextBuff,D0
            MOVE.L D0,MaxAddress
            JSR IsItVisibl                  * Find out whether current line is visible.
            RTS


* FUNCTION GetMsLnNo. This calculates the legal line number that the mouse is pointing at, assuming that the Y co-ordinate of the line
*                     the mouse would be pointing at is in YCo_ord.

GetMsLnNo:  JSR FindLineNo                  * Get line number (don't know whether it exists yet).
            CMP.W NumLines,D0               * Is the line past the last text line?
            BLE NotTooFar                   * Branch if no.
            MOVE.W NumLines,D0              * Otherwise, set line to be last line.
NotTooFar:  MOVE.W D0,LineNumber            * Save the value calculated.
            RTS

* SUBROUTINE SetCrntVrs. This routine sets the variables for the current line.

SetCrntVrs: MOVE.L TextBuff,CrntLnStrt
            MOVE.B BuffLength,CrntLnLen
            MOVE.B CursorYPos,CurntLnYCo
            MOVE.W LineNumber,CurrntLine    * Get info for current line.
            RTS


* FUNCTION IsItVisibl. This routine returns Yes in D0 if the line refered to in LineNumber is visible on screen (else D0 = No).

IsItVisibl: MOVE.B #No,D0                   * Assume the answer will be no.
            MOVE.W TopLine,D1               * Is line too high up?
            ADDQ.W #1,D1
            CMP.W LineNumber,D1
            BGT IsVisEnd                    * Branch if yes.
            ADDI.W #20,D1                   * Is line too low down?
            CMP.W LineNumber,D1
            BLT IsVisEnd                    * Branch if yes.
            MOVE.B #Yes,D0                  * Otherwise, line is visible.
IsVisEnd:   RTS


* SUBROUTINE WordLeft. This routine moves the cursor one word leftwards.

WordLeft:   MOVE.B CursorXPos,Position      * Set search start position.
            JSR NxtWrdLeft
            MOVE.B Position,NewCrsrXCo      * Move cursor to new position.
            RTS


* SUBROUTINE WordRight. This routine moves the cursor one word rightwards.

WordRight:  MOVE.B CursorXPos,Position      * Set search start position.
            JSR NxtWrdRyte         
            MOVE.B Position,NewCrsrXCo      * Move cursor to new position.
            RTS


* SUBROUTINE ShBackspc. This routine is called when the user presses Shift + Backspace in the main editing loop. It allows the
*                       user to backspace to the end of the previous line.

ShBackspc:  TST.B CursorXPos                * Is the cursor at the start of the line?
            BEQ NxtSBSTest                  * Branch if yes.
Backspace:  TST.B CursorXPos                * Ordinary Backspace JSR's to here, so check is required again...
            BEQ NoBackspc                   * (if at start of line branch, as nothing happens)
            DC.W HideMouse
            JSR CursrOff
            JSR BackspcAc
            MOVE.B CursorYPos,NewCrsrYCo    * Set y co-ordinate.
            MOVE.B CursorYPos,Line          * Delete all from last character on line to end of screen line...
            MOVE.B BuffLength,Column
            JSR MoveCurs
            JSR WipeLine
            JSR CrsrMvAc                    * Display the changed part of the line...
            CLR.L D0
            MOVE.B NewCrsrXCo,D0
            ADD.L TextBuff,D0
            MOVE.L D0,SrcPtr
            JSR PrntStr
            BRA SBSEnd
NoBackspc:  NOP
NoShBkspc:  RTS
NxtSBSTest: MOVE.B #No,ScrnChange           * Assume attempt will fail.
            CMPI.W #1,CurrntLine            * Is the cursor on the first line of text?
            BEQ NoShBkspc                   * Branch if yes as nothing happens.
            MOVE.W CurrntLine,LineNumber    * Join the two lines (the one the cursor is on and the one before)...
            JSR JoinLine
            CMPI.B #No,ScrnChange           * Was the join allowed?
            BNE ShBckspcOk                  * Branch if yes.
            MOVE.L TextBuff,SrcPtr          * Otherwise, reset BuffLength and exit...
            JSR FindBufLen
            RTS
ShBckspcOk: DC.W HideMouse                  * Mouse and cursor off...
            JSR CursrOff
            CMPI.B #80,Position             * Is the newly joined line full? (if so, no room for cursor!)
            BNE LineNotFul                  * Branch if no.
            MOVE.L TextBuff,A0              * Otherwise, delete last character in newly joined line...
            CLR.B -2(A0)
LineNotFul: CMPI.B #4,CursorYPos            * Is the cursor at the top of the text window area?
            BEQ AtTopOfTWA                  * Branch if yes, as this is a special case.
            MOVE.W CurrntLine,LineNumber    * Otherwise, update the screen display...
            JSR DelScrnLn
            CLR.L D0
            MOVE.B Position,D0
            ADD.L TextBuff,D0
            SUBI.L #81,D0
            MOVE.L D0,SrcPtr
            MOVE.B CursorYPos,Line
            SUBQ.B #1,Line
            MOVE.B Position,Column
            JSR MoveCurs
            JSR WipeLine
            JSR PrntStr
            MOVE.W TopLine,LineNumber       * (now show line appearing at bottom, if any)
            ADDI.W #21,LineNumber
            JSR LineToScrn
            MOVE.B CursorYPos,NewCrsrYCo    * Set new y co-ordinate.
            SUBI.B #1,NewCrsrYCo
SBSCont:    MOVE.B Position,NewCrsrXCo      * Set new x co-ordinate.
            MOVE.B #Yes,LineChange          * Set line change flag.
SBSEnd:     JSR CrsrMvAc                    * Move the cursor to new position...
            MOVE.B #Yes,RemNewXPos          * Set flag to reset remembered x position.
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS
AtTopOfTWA: MOVE.W TopLine,LineNumber       * Show new line now visible at top of TWA...
            SUBI.W #1,TopLine               * (new TopLine must be set before ShowLine call)
            JSR LineToScrn
            MOVE.B CursorYPos,NewCrsrYCo    * y co-ordinate doesn't change.
            BRA SBSCont


* SUBROUTINE ShftDelete. This routine is called when the user presses Shift + Delete in the main editing loop. It allows the
*                        user to drag the line below line CurrntLine onto the end of line CurrntLine (this is equivalent to moving
*                        the cursor to the start of the next line and pressing Shift + Backspace, except if the action is not
*                        allowed, the cursor does not move from the current line).

ShftDelete: MOVE.B BuffLength,D0            * Is the cursor at the end of the current line?
            CMP.B CursorXPos,D0
            BEQ NxtSDlTest                  * Branch if yes.
Delete:     MOVE.B BuffLength,D0            * Ordinary Delete JSR's to here, so check is required again...
            CMP.B CursorXPos,D0
            BEQ NoDelete                    * (If cursor at end of line branch, as nothing happens)
            DC.W HideMouse
            JSR CursrOff
            JSR DeleteAc
            MOVE.B BuffLength,NewCrsrXCo    * Now update screen...
            MOVE.B CursorYPos,NewCrsrYCo
            JSR CrsrMvAc
            JSR WipeLine
            MOVE.B CursorXPos,NewCrsrXCo
            JSR CrsrMvAc
            CLR.L D0
            MOVE.B CursorXPos,D0
            ADD.L TextBuff,D0
            MOVE.L D0,SrcPtr
            JSR PrntStr
            BRA SDelEnd
NoDelete:
NoShftDel:  RTS
NxtSDlTest: MOVE.B #No,ScrnChange           * Assume attempt will fail.
            MOVE.W NumLines,D0              * Is the cursor on the last line of text?
            CMP.W CurrntLine,D0
            BEQ NoShftDel                   * Branch if yes as nothing happens.
            MOVE.W CurrntLine,LineNumber    * Join the two lines (the one the cursor is on and the one after)...
            ADDQ.W #1,LineNumber
            JSR JoinLine
            CMPI.B #No,ScrnChange           * Was the join allowed?
            BNE ShDeleteOk                  * Branch if yes.
            MOVE.L TextBuff,SrcPtr          * Otherwise, reset BuffLength and exit...
            JSR FindBufLen
            RTS
ShDeleteOk: DC.W HideMouse                  * Mouse and cursor off...
            JSR CursrOff
            CMPI.B #24,CursorYPos           * Is the cursor at the bottom of the text window area?
            BEQ BottmOfTWA                  * Branch if yes, as this is a special case.
            JSR DelScrnLn                   * Otherwise, update the screen display (first, scroll text at bottom up one)...
            CLR.L D0
            MOVE.B Position,D0
            ADD.L TextBuff,D0
            MOVE.L D0,SrcPtr
            MOVE.B CursorYPos,Line
            MOVE.B CursorXPos,Column
            JSR MoveCurs
            JSR PrntStr
            MOVE.W TopLine,LineNumber       * (now show line appearing at bottom, if any)
            ADDI.W #21,LineNumber
            JSR LineToScrn
SDelCont:   MOVE.B CursorYPos,NewCrsrYCo    * Set co-ordinates (they don't actually change)...
            MOVE.B CursorXPos,NewCrsrXCo
            MOVE.B #Yes,LineChange          * Set line change flag.
SDelEnd:    JSR CrsrMvAc                    * Move the cursor to new position...
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS
BottmOfTWA: MOVE.W CurrntLine,LineNumber    * Show new line now visible at bottom of text area window...
            JSR LineToScrn
            BRA SDelCont                    * Jump to identical code.


* SUBROUTINE Return. This routine is called when the user presses Return in the main editing loop. If the current line is the very
*                    last line of available text in the memory, nothing will happen and ScrnChange returns No; otherwise,
*                    ScrnChange returns Yes.

Return:     MOVE.W MaxNoLines,D0            * Is the cursor on the last line of available memory?
            CMP.W CurrntLine,D0
            BNE ReturnIsGo                  * Branch if no.
            MOVE.B #No,ScrnChange           * Otherwise, clear flag & exit...
            RTS
ReturnIsGo: DC.W HideMouse                  * Mouse & cursor off...
            JSR CursrOff
            MOVE.W CurrntLine,LineNumber    * Set parameters for, and then call, CutLine...
            MOVE.B CursorXPos,Position
            JSR CutLine
            MOVE.B CursorYPos,Line          * Clear all characters from the cursor position onwards on the current text line...
            MOVE.B CursorXPos,Column
            JSR MoveCurs
            JSR WipeLine
            MOVE.B #No,NewLnTaken           * Clear flag.
CharVector: MOVE.W CurrntLine,LineNumber    * Get the physical line number of the newly created line...
            ADDQ.W #1,LineNumber
            CMPI.B #24,CursorYPos           * Is the cursor at the bottom of the screen?
            BNE NotAtBtTWA                  * Branch if no.
            MOVE.B #4,Line                  * Otherwise, make way for new line at bottom of text area window.
            CLR.B Column
            JSR MoveCurs
            JSR RemoveLine
            ADDQ.W #1,TopLine
            BRA ReturnCont
NotAtBtTWA: JSR InsScrnLn                   * Insert a blank line on the screen to make way for the newly created line.
ReturnCont: JSR LineToScrn
            MOVE.B CursorYPos,NewCrsrYCo    * Set new cursor y-co...
            ADDQ.B #1,NewCrsrYCo
            CLR.B NewCrsrXCo                * Set new cursor x-co.
            MOVE.B #Yes,LineChange          * Set line change flag.
            MOVE.B #Yes,RemNewXPos          * Set flag to reset remembered x position.
            JSR CrsrMvAc                    * Now move cursor to new position...
            RTS


* SUBROUTINE KillLine. This routine clears the current text line.

KillLine:   TST.B BuffLength                * Is the line empty?
            BNE GoKillLine                  * Branch if no.
            RTS
GoKillLine: DC.W HideMouse
            JSR CursrOff
            MOVE.L TextBuff,SrcPtr          * Clear the current text line (memory only)...
            MOVE.B BuffLength,LineLen
            JSR ClearData
            CLR.B Column                    * Clear the line on the screen...
            MOVE.B CursorYPos,Line
            JSR MoveCurs
            JSR WipeLine
            MOVE.B #Yes,RemNewXPos          * Set flag to reset remembered x position.
            CLR.B BuffLength                * Line is now empty.
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS


* SUBROUTINE DeleteLine. This routine totally deletes the current line (similar to KillLine followed by Shift + Backspace).

DeleteLine: DC.W HideMouse                  * First delete the line on screen...
            JSR CursrOff
            MOVE.W CurrntLine,LineNumber
            JSR DelScrnLn
            MOVE.L TextBuff,SrcPtr          * Now copy the current line to DelLnBuf...
            MOVE.L #DelLnBuf,DstPtr
            MOVE.B BuffLength,LineLen
            MOVE.B BuffLength,DLBfLength    * (set no. of characters in DelLnBuf)
            JSR CopyData
            MOVE.W NumLines,D0              * Is the cursor on the last line of text?
            CMP.W CurrntLine,D0
            BEQ OnLastLine                  * Branch if yes.
            MOVE.W CurrntLine,ShntLnStrt    * Otherwise, delete the line in memory...
            ADDQ.W #1,ShntLnStrt
            MOVE.W #1,ShuntDist
            JSR ShuntUp
            MOVE.W TopLine,LineNumber       * Next show the line appearing at bottom of screen, if any,...
            ADDI.W #21,LineNumber
            JSR LineToScrn
            MOVEA.L TextBuff,A0             * Set A0 ready for branch to CursCont.
            BRA CursCont
OnLastLine: JSR ClearData                   * Delete the line in memory...
            CMPI.W #1,CurrntLine            * Is the cursor on the first line of text?
            BEQ NoNmLnsDec                  * Branch if yes.
            SUBQ.W #1,NumLines              * Otherwise, decrease the number of lines in the TFM.
NoNmLnsDec: MOVE.B #Yes,LineChange          * Set flag to denote cursor on a new line.
            JSR CursorUp                    * Move the cursor up to the previous line.
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS


* SUBROUTINE UndoDelLn. This routine replaces a line that has been previously deleted with DeleteLine. Note that it has been
*                       designed to allow a simple one line block move facility (by not caring whether the cursor has moved to a
*                       different line since the line was removed by UndoDelLn.

UndoDelLn:  DC.W HideMouse
            JSR CursrOff
            MOVE.W CurrntLine,ShntLnStrt    * First shunt down text in memory...
            MOVE.W #1,ShuntDist
            JSR ShuntDown
            MOVE.L #DelLnBuf,SrcPtr         * Then copy the previously deleted line to the current line...
            MOVE.L CrntLnStrt,DstPtr
            MOVE.B DLBfLength,LineLen
            JSR CopyData
            JSR ClearData                   * Then delete data stored at DelLnBuf.
            MOVE.W CurrntLine,LineNumber    * Now place a blank line on screen...
            JSR InsScrnLn
            JSR LineToScrn                  * Next display retrieved line.
            JSR GetLineInf                  * Now set new cursor position...
            MOVE.B CursorXPos,NewCrsrXCo
            MOVE.B CurntLnYCo,NewCrsrYCo
            JSR GetOkXCo
            JSR CrsrMvAc
            MOVE.B #Yes,LineChange          * Set flag.
            RTS


* SUBROUTINE Help. This routine displays helpful information. After a user prompt has been acknowledged, the text is re-shown.

Help:       MOVE.B CursorXPos,-(sp)         * Save current cursor position...
            MOVE.W CursorYPos,-(sp)
            MOVEM.L D4/A4,-(sp)             * SRTBU
            DC.W HideMouse
            JSR CursrOff
            JSR OnOverflow
            MOVE.B #1,FieldWidth            * Set parameter for PrintNum.
            CLR.L D4                        * Set page counter.
            LEA HelpList,A4                 * Set pointer to help address list.
MoreHelp:   ADDQ.B #1,D4                    * Increase page counter.
            MOVE.L #HlpPageMsg,SrcPtr       * Display new help message...
            JSR PrntStr
            MOVE.L D4,Value                 * (show which page...)
            JSR Convert
            MOVE.B #2,FieldWidth
            JSR PrintNum
            JSR ClrTxtWndw                  * Clear text window area ready for next page of help info.
            MOVE.L (A4)+,SrcPtr             * Display next page of help information...
            JSR PrntStr
            CMP.B HelpPages,D4              * Was that the last page?
            BNE QMoreHelp                   * Branch if no.
            MOVE.B #38,NewCrsrXCo           * Otherwise, wait for acknowledgement of prompt and exit...
            MOVE.B #23,NewCrsrYCo
            JSR MouseWait
            BRA HelpEnd
QMoreHelp:  MOVE.L #NxtPageMsg,SrcPtr       * Get user to decide whether next page is to be shown...
            JSR PrntStr
            MOVE.B #36,NewCrsrXCo           * Set parameters ready for 'More...' message...
            MOVE.B #24,NewCrsrYCo
            JSR MoreMessg
            CMPI.B #Yes,Cancelled           * Does the user want to see the next page of help info?
            BNE MoreHelp                    * Branch if yes.
HelpEnd:    JSR ShowText
            MOVEM.L (sp)+,D4/A4             * RRU
QMVector:   MOVE.L #ScrnDsply2,SrcPtr       * Re-show 2nd line of screen display...
            JSR PrntStr
            MOVE.B (sp)+,Line               * Move cursor back to original position...
            MOVE.B (sp)+,Column
            JSR MoveCurs
            JSR OffOvrflow
            RTS
          

* SUBROUTINE DeskInfo. This routine justs displays some information concerning this program (author, et.c.).

DeskInfo:   DC.W HideMouse                  * Set up info window.
            JSR CursrOff
            MOVE.B CursorXPos,-(sp)         * Save cursor co-ords...
            MOVE.B CursorYPos,-(sp)
            MOVE.L #DskInfTitl,SrcPtr
            JSR PrntStr
            JSR ClrTxtWndw
            MOVE.L #BasicWndw,SrcPtr
            JSR PrntStr
            MOVE.L #DeskInfMsg,SrcPtr       * Display info.
            JSR PrntStr
            MOVE.B #38,NewCrsrXCo           * Wait for user to acknowledge prompt...
            MOVE.B #19,NewCrsrYCo
            JSR MouseWait
            JSR ShowText                    * Re-show text.
            BRA QMVector


* SUBROUTINE QuitMainEd. If the user has pressed Alternate + Q, this routine ask the user whether she/he really wants to quit the
*                        program. YNAnswer returns the answer, either Yes or No.

QuitMainEd: DC.W HideMouse                  * Set up info window.
            JSR CursrOff
            MOVE.B CursorXPos,-(sp)         * Save cursor co-ords...
            MOVE.B CursorYPos,-(sp)
            JSR DelTosBox
            MOVE.L #QuitTitle,SrcPtr
            JSR PrntStr
            MOVE.L #BasicWndw,SrcPtr
            JSR PrntStr
            MOVE.L #QuitMessg,SrcPtr        * Display quit message with 'are you sure' prompt...
GetYNVectr: JSR PrntStr
            MOVE.L #AYSMessage,SrcPtr
            JSR PrntStr
            MOVE.L #$0C390E39,YNCoords      * Set parameter for GetYorNAns. (Yes at line 12, col. 57; No at line 14, col. 57)
            JSR GetYorNAns
            BRA QMVector                    * Move cursor back to original position (jump to identical code).


* SUBROUTINE ClearFile. This routine clears the text file, but only if the user answers yes to a prompt. The routine is called if
*                       the user presses the 'clear-whole-text-file' key.

ClearFile:  DC.W HideMouse                  * Set up info window.
            JSR CursrOff
            MOVE.B CursorXPos,-(sp)         * Save cursor co-ords...
            MOVE.B CursorYPos,-(sp)
            JSR DelTosBox
            MOVE.L #BasicWndw,SrcPtr
            JSR PrntStr
            BTST.B #EthrShftBit,KeybrdStat  * Has Shift been pressed as well?
            BEQ ClrFileSet                  * Branch if no.
            MOVE.L #NewFlTitle,-(sp)        * Set addresses of strings to be shown...
            MOVE.L #NewFileMsg,-(sp)
            BRA FilePtrSet
ClrFileSet: MOVE.L #ClrFlTitle,-(sp)        * Set addresses of strings to be shown...
            MOVE.L #ClrFileMsg,-(sp)
FilePtrSet: MOVE.L (sp)+,SrcPtr             * Display messages (for either Clear File or New File)...
            JSR PrntStr
            MOVE.L (sp)+,SrcPtr
            BRA GetYNVectr                  * Branch to identical code.


* SUBROUTINE FlipOvMode. This routine toggles the Overflow flag (on the screen as well).

FlipOvMode: MOVE.B Overflow,D1              * Switch Overflow flag...
            JSR GetNewFlag
            MOVE.B D0,Overflow
            MOVE.L #OvrflwSpec,TxtBlkSpec   * Move cursor to message start position...
            JSR SetTBSpec
            MOVE.B #4,NewCrsrXCo
SendMessg:  JSR CursrOff
            MOVE.B TxtBlkLeft,D0
            ADD.B D0,NewCrsrXCo
            MOVE.B #2,NewCrsrYCo
            JSR CrsrMvAc
            JSR PrntStr                     * New flag state dispplayed.
            MOVE.B CursorXPos,NewCrsrXCo    * Move cursor back to original position.
            MOVE.B CursorYPos,NewCrsrYCo
            JSR CrsrMvAc
            RTS


* SUBROUTINE FlipWPMode. This routine toggles the Word Processor Mode flag (on the screen as well).

FlipWPMode: MOVE.B WrdProMode,D1            * Switch Word Processor flag...
            JSR GetNewFlag
            MOVE.B D0,WrdProMode
            MOVE.L #WrdProSpec,TxtBlkSpec   * Move cursor to message start position...
            JSR SetTBSpec
            MOVE.B #3,NewCrsrXCo
            BRA SendMessg


* This routine is just a piece of code used by FlipOvMode and FlipWPMode placed into a subroutine to avoid code repetition.
* The current state of the flag to be switched is placed in D1 before the call. Afterwards, the new state of the flag is in D0,
* and the address of the new string to be printed is in SrcPtr.

GetNewFlag: MOVE.B #No,D0                   * Switch flag...
            MOVE.L #OffMessage,SrcPtr
            CMPI.B #Yes,D1
            BEQ FlagFlippd
            MOVE.B #Yes,D0
            MOVE.L #OnMessage,SrcPtr
FlagFlippd: RTS


* SUBROUTINE FlipInsert. This routine is called by the main editor to switch the text entering mode (insert/replace).

FlipInsert: JSR CursrOff
            JSR InsertAc                    * Switch the entering mode...
            RTS
            

* SUBROUTINE GetOkXCo. This routine returns a valid x co-ordinate for a text line based on what the cursor is trying to be set to.
*                      TextBuff must contain the address of the start of the line.
*                      BuffLength must contain the length of the line.
*                      NewCrsrXCo must contain the attempted x co-ordinate.
*                      Afterwards, the legal x co-ordinate is returned in NewCrsrXCo.

GetOkXCo:   MOVEA.L TextBuff,A0                     * Is there a chracter at the wanted x co.?
            CLR.W D0
            MOVE.B NewCrsrXCo,D0
            TST.B 0(A0,D0.W)
            BEQ SetToBfLen                          * Branch if no.
            RTS
SetToBfLen: MOVE.B BuffLength,NewCrsrXCo            * Set to length of text line.
            RTS
            

* SUBROUTINE Character. This routine is called when the user presses a valid character key in the main editing loop. Note that if
*                       Overflow is Off, the editor is effectively acting as a single-line-text-editor on the current line.

Character:  DC.W HideMouse                  * Mouse and cursor off...
            JSR CursrOff
            JSR CharAc                      * Insert the character.
            MOVE.B #Yes,ScrnChange          * Set flags...
            MOVE.B #Yes,RemNewXPos
            MOVE.B MaxCrsrXCo,D0            * Was cursor at last position in line?
            CMP.B CursorXPos,D0
            BEQ NewLineChk                  * Branch if yes.
            CMPI.B #No,WrdProMode           * Is Word Processor mode on?
            BEQ NoOvrflw                    * Branch if no.
            TST.B CursorXPos                * Is the cursor at the start of the text line?
            BEQ NextWWTest                  * Branch if yes to next Word Wrap test.
            MOVE.B #No,NewLnTaken           * Otherwise, clear flag.
NextWWTest: CMPI.B #No,NewLnTaken           * Has typing just oveflowed from previous line?
            BEQ NoOvrflw                    * Branch if no.
            MOVE.W NwLnTknLn,D0             * Is the cursor on the line after the line on which the NewLnTaken flag was set?
            CMP.W CurrntLine,D0             * Branch if yes.
            BEQ WrdWrapTst
            MOVE.B #No,NewLnTaken           * Otherwise, clear flag and branch.
            BRA NoOvrflw
WrdWrapTst: CMPI.B #' ',NextChar            * Was the character just typed a space?
            BNE GoWordWrap                  * Branch if no, as there is a split word to be recovered.
            MOVEA.L TextBuff,A0             * Otherwise, delete the space...
            CLR.B (A0)
            MOVE.B #No,NewLnTaken           * ...clear the flag...
            CLR.B BuffLength                * ...reset line length and exit...
            RTS
GoWordWrap: MOVE.L D6,-(sp)                 * SRTBU
            MOVE.L CrntLnStrt,SrcPtr        * Get length of previous line...
            SUBI.L #81,SrcPtr
            JSR FindBufLen
            MOVE.L SrcPtr,TextBuff          * Find last space in previous line...
            JSR NxtSpcLeft
            CLR.L D6                        * Calculate no. of characters in split word to copy...
            MOVE.B BuffLength,D6
            SUB.B Position,D6
            CMP.B BuffLength,D6             * Is that the whole line?
            BNE WWContinue                  * Branch if no, word wrap is go.
            MOVE.L CrntLnStrt,TextBuff      * Otherwise, reset variables and exit...
            MOVE.B CrntLnLen,BuffLength
            MOVE.L (sp)+,D6
            BRA NoOvrflw
WWContinue: SUBQ.B #1,D6                    * Set no. of characters to copy from previous line...
            MOVE.B D6,LineLen
            CLR.L D0                        * Set source and...
            MOVE.B Position,D0
            ADDQ.L #1,D0
            ADD.L D0,SrcPtr
            MOVE.L CrntLnStrt,DstPtr        * ...destination for copying.
            JSR CopyData
            SUBQ.L #1,SrcPtr                * Clear garbage at end of previous line (subtracting one clears the dangling space)...
            ADDQ.B #1,LineLen
            MOVE.B LineLen,CursorXPos       * (this also happens to be the new x co-ordinate)
            JSR ClearData
            ADD.L CrntLnStrt,D6             * Place character just typed at end of new line...
            MOVEA.L D6,A0
            MOVE.B NextChar,(A0)
            MOVE.W CurrntLine,LineNumber    * Update previous line...
            SUBQ.W #1,LineNumber            * Display current line...
            MOVE.B #Yes,ClrOldChrs          * (set flag to clear old garbage)
            JSR LineToScrn
            MOVE.W CurrntLine,LineNumber    * Display current line...
            JSR LineToScrn
            MOVE.L CrntLnStrt,TextBuff      * Reset variables, et.c.,...
            MOVE.B #No,NewLnTaken           * Clear flag.
            MOVE.B #Yes,LineChange          * Set flag.
            MOVE.L (sp)+,D6                 * RRU
            RTS
NoOvrflw:   MOVE.B CursorXPos,NewCrsrXCo    * Otherwise, set new cursor position...
            ADDQ.B #1,NewCrsrXCo
            MOVE.B CursorYPos,NewCrsrYCo
LnUpdate:   MOVE.B CursorXPos,Column
            MOVE.B CursorYPos,Line
            JSR MoveCurs
            CLR.L D0                        * Update the current line...
            MOVE.B CursorXPos,D0
            ADD.L CrntLnStrt,D0
            MOVE.L D0,SrcPtr
            JSR PrntStr
            JSR CrsrMvAc                    * Now move the cursor to the new position...
            RTS
NewLineChk: CMPI.B #No,Overflow             * Is character overflow on?
            BEQ NoOvrflw                    * Branch if no.
            MOVE.W MaxNoLines,D0            * Are there any more text lines available in memory?
            CMP.W NumLines,D0
            BEQ NoOvrflw                    * Branch if no.
            JSR LnUpdate                    * Update the current line.
            MOVE.W CurrntLine,LineNumber    * Create new line in file...
            MOVE.B MaxLineLen,Position
            JSR CutLine
            MOVE.B #Yes,NewLnTaken          * Set flag for word processor mode (not needed if Off, but never mind).
            MOVE.W CurrntLine,NwLnTknLn     * Set line on which typing should continue to activate word grab...
            ADDQ.W #1,NwLnTknLn
            BRA CharVector                  * Branch to identical code.


* SUBROUTINE RestorLine. This routine replaces the contents of the current line with the contents of the Undo buffer (which is
*                        then 'cleared').

RestorLine: DC.W HideMouse
            JSR CursrOff
            MOVE.L TextBuff,SrcPtr          * Clear current contents of current line...
            MOVE.B MaxLineLen,LineLen
            JSR ClearData
            MOVE.L TextBuff,DstPtr          * Now copy Undo buffer contents to current line...
            MOVE.L #UndoBuffer,SrcPtr
            MOVE.B UndoBufLen,LineLen
            JSR CopyData
            MOVE.B CursorYPos,BuffStY
            JSR UpdateLine
            MOVE.B CursorYPos,NewCrsrYCo    * Move the cursor to new position...
            MOVE.L TextBuff,A0              * (set A0 ready for JSR...)
            JSR CursCont
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS


* FUNCTION GetLineYCo. This routine calculates what the y co-ordinate of a given text line should be based on its relative
*                      position to TopLine. LineNumber must contain the physical line number required.

GetLineYCo: MOVE.W LineNumber,D0
            SUB.W TopLine,D0
            ADDI.W #3,D0
            RTS


* FUNCTION FindLineNo. This routine does the opposite of GetLineYCo. Given a Y co-ordinate, the physical text line supposedly
*                      at that y co-ordinate is returned in D0. YCo_ord must contain the y co-ordinate in question.

FindLineNo: CLR.W D0
            MOVE.B YCo_ord,D0
            SUBQ.B #3,D0
            ADD.W TopLine,D0
            RTS


* SUBROUTINE SaveCurrnt. This routine saves the contents of the current line to the Undo buffer.

SaveCurrnt: MOVE.L CrntLnStrt,SrcPtr        * Take copy of line for Undo function...
            MOVE.L #UndoBuffer,DstPtr
            MOVE.B CrntLnLen,LineLen
            JSR CopyData
            MOVE.B CrntLnLen,UndoBufLen     * Set number of characters in Undo buffer.
            RTS


* SUBROUTINE SaveCrsPos. This routine saves the current position of the cursor in the text file (line and cursor x co-ordinate).

SaveCrsPos: MOVE.W CurrntLine,D0            * Has the current position already been saved?
            CMP.W SavedLine,D0
            BNE GoSaveCsPs                  * Branch if no here...
            MOVE.B CursorXPos,D0
            CMP.B SavedXPos,D0
            BNE GoSaveCsPs                  * ...or here.
            RTS
GoSaveCsPs: JSR FlashCursr                  * Flash the word 'Cursor' on line 2 to show that the position save is happening.
            MOVE.B CursorYPos,YCo_ord       * Get line number and save...
            JSR FindLineNo
            MOVE.W D0,SavedLine
            MOVE.B CursorXPos,SavedXPos     * Save current cursor x co-ordinate.
            RTS


* SUBROUTINE JumpToSavd. This routine moves the cursor to the place in the text file stored by SaveCrsPos (if possible).

JumpToSavd: TST.W SavedLine                 * Is there a saved position to jump to?
            BMI CantJTS                     * Branch if no.
            MOVE.W CurrntLine,D0            * Is the cursor already at the saved position?
            CMP.W SavedLine,D0
            BNE NotAtSaved                  * Branch if no here...
            MOVE.B CursorXPos,D0
            CMP.B SavedXPos,D0
            BNE NotAtSaved                  * ...or here.
CantJTS:    RTS
NotAtSaved: MOVE.W NumLines,D0              * Does the saved line exist?
            CMP.W SavedLine,D0
            BGE GoJump                      * Branch if yes.
SetToSilly: MOVE.W #-1,SavedLine            * Otherwise, set position save variables to ridiculous values and exit...
            MOVE.B #-1,SavedXPos
            RTS
GoJump:     JSR FlashCursr                  * Flash the word 'Cursor' to show that the jump is happening.
            MOVE.W SavedLine,LineNumber     * Is the line to be jumped to visible?
GotoLnVctr: JSR IsItVisibl
            CMPI.B #Yes,D0
            BEQ GoRestore                   * Branch if yes.
            JSR JumpToLine                  * Otherwise, make the line visible.
GoRestore:  MOVE.W SavedLine,LineNumber     * Get y co-ordinate of line...
            JSR GetLineYCo
            MOVE.B D0,NewCrsrYCo
            JSR GetLineInf                  * Set the x co-ordinate the cursor is to move to...
            MOVE.B SavedXPos,D0             * If saved cursor position is past end of line, set to end of line...
            CMP.B BuffLength,D0
            BLE SavedPosOk
            MOVE.B BuffLength,D0
SavedPosOk: MOVE.B D0,NewCrsrXCo            * Set new cursor position.
            JSR CursrOff
            MOVE.L #ScrnDsply2,SrcPtr       * (show help message, this is for GotoLine)
            JSR PrntStr
            JSR CrsrMvAc                    * Now move the cursor to the new position...
            MOVE.B #Yes,LineChange          * Set line change flag.
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS


* SUBROUTINE FlashCursr. This routine flashes the word 'Cursor' on line 2 of the screen by printing the word in inverse text and
*                        then printing the word in non-inverse text.

FlashCursr: DC.W HideMouse
            JSR CursrOff
            DC.W InitA                     * Save current cursor position (musn't destroy CursorX/YPos, so do it directly)...
            MOVE.B -27(A0),-(sp)
            MOVE.B -25(A0),-(sp)
            MOVE.L #CursInvrtd,SrcPtr       * Display 'Cursor' in inverse...
            JSR PrntStr
            JSR Pause                       * A short wait so the flash is visible.
            MOVE.L #CursNormal,SrcPtr       * Display 'Cursor' in non-inverse...
            JSR PrntStr
            MOVE.B (sp)+,Line               * Move cursor back to original position...
            MOVE.B (sp)+,Column
            JSR MoveCurs
            RTS


* SUBROUTINE JumpToLine. This routine jumps the text window area (TWA) to a requested line so that, if possible, the line is in
*                        the middle of TWA; e.g. a Go-to-line function would use this routine.
*                        LineNumber must contain the physical line to jump to (Range: 1..NumLines).

JumpToLine: MOVE.W LineNumber,D0            * For the line to be in the middle of the TWA, TopLine = LineNumber - 11...
            SUBI.W #11,D0
            BPL LineNumOk                   * Branch if line no. is valid so far.
            CLR.W D0
LineNumOk:  MOVE.W D0,D1                    * Is the line that would appear at the bottom of the screen non-existent?
            ADDI.W #20,D1
            CMP.W NumLines,D1
            BLE LineNumOk2                  * Branch if no.
            MOVE.W NumLines,D0              * Otherwise, set TopLine so that last text line is at bottom of screen...
            SUB.W #20,D0
            BPL LineNumOk2                  * If this is o.k., branch.
            CLR.W D0                        * Otherwise, just TopLine to be at the top of the text.
LineNumOk2: MOVE.W D0,TopLine               * Now update text screen based on new TopLine...
            DC.W HideMouse
            JSR CursrOff
            JSR ShowText
            MOVE.B #Yes,LineChange          * Set line change flag.
            RTS


* SUBROUTINE GotoLine. This routine allows the user to goto any valid line in the text file.

GotoLine:   CMPI.W #1,NumLines              * Is there just one line?
            BNE GLCont1                     * Branch if no.
            RTS
GLCont1:    DC.W HideMouse
            JSR CursrOff
            MOVE.B MaxLineLen,-(sp)         * Save to prevent corruption.
            MOVE.B CursorXPos,D5
            MOVE.L #GotoLnTitl,SrcPtr       * Show Goto Line title to replace Help message...
            JSR PrntStr
            JSR DelTosBox                    * Set up Goto Line window...
            MOVE.L #BasicWndw,SrcPtr
            JSR PrntStr
            MOVE.L #GotoLnMsg1,SrcPtr
            JSR PrntStr
            CLR.L D0
            MOVE.W NumLines,D0
            MOVE.L D0,Value
            JSR Convert
            MOVE.L NumSrc,SrcPtr
            JSR PrntStr
            MOVE.L #GotoLnMsg2,SrcPtr
            JSR PrntStr
            MOVE.L #GotoLineBf,NumSrc       * Get the line number the user wants to jump to...
            MOVE.L NumSrc,SrcPtr
GetNmAgain: JSR FindBufLen
            MOVE.B BuffLength,NumDigits
            JSR GetValue                    * (get value of old number in goto-line buffer)
            CMPI.B #No,InvalidChr           * Was there a bad character?
            BEQ GotoLnCont                  * Branch if no.
            MOVE.B #'1',GotoLineBf          * Otherwise, put a valid number in the buffer and branch back...
            CLR.B GotoLineBf+1
            BRA GetNmAgain
GotoLnCont: JSR Convert
            MOVE.L NumSrc,SourcePtr
            MOVE.B #Yes,UseOld
            MOVE.B #No,IgnoreOld
            MOVE.L #GotoLineBf,TextBuff
            MOVE.B #5,MaxLineLen
            MOVE.B #No,UseMessage
            MOVE.B #Yes,InsrtState
            MOVE.B #No,TempBuffSt
            MOVE.B #13,BuffStY
            JSR SaveTmpBf                   * Preserve EnterLine buffer.
            JSR EnterLine                   * Get line number.
            JSR RestrTmpBf                  * Restore EnterLine Buffer.
            JSR CursrOff
            CMPI.B #Yes,Cancelled           * Has the user cancelled this function?
            BEQ CancGotoLn                  * Branch if yes.
            MOVE.L #GotoLineBf,SrcPtr       * Convert the given number string to a value...
            MOVE.L SrcPtr,NumSrc            * (used to calculate, partially, the number of digits).
            MOVE.L SrcPtr,TextBuff          * Get length of buffer...
            JSR FindBufLen
            CLR.B Position                  * (find first digit/character...)
            JSR NxtChrRyte
            MOVE.B Position,D0              * Is there a non-space character?
            CMP.B BuffLength,D0
            BNE NonSpcFnd                   * Branch if yes.
            ADDQ.W #6,sp                    * Otherwise, CS and branch out...
            BRA CancGotoLn
NonSpcFnd:  CLR.L D0                        * (set NumSrc to point to first non-space character...)
            MOVE.B Position,D0
            ADD.L SrcPtr,D0
            MOVE.L D0,NumSrc
            MOVE.B BuffLength,Position      * (find last digit/character...)
            JSR NxtChrLeft
            MOVE.B Position,NumDigits
            ADDQ.B #1,NumDigits
            MOVE.L NumSrc,D0                * (if there were any leading spaces before first digit, NumDigits must be corrected...
            SUB.L #GotoLineBf,D0
            SUB.B D0,NumDigits              * (if there were no leading spaces, D0 would be zero, so NumDigits would be set o.k.)
            JSR GetValue
            TST.L Value                     * Is the number illegal?
            BEQ CancGotoLn                  * Branch if yes.
            MOVE.L Value,D1
            MOVE.W D1,Value
            MOVE.W NumLines,D0              * Does the given line exist?
            CMP.W D1,D0
            BLT CancGotoLn                  * Branch if no.
            MOVE.B (sp)+,MaxLineLen         * Restore variable.
            MOVE.B #Yes,UseMessage
            JSR FillTosBox                  * Clear messages, e.t.c....
            MOVE.W Value,LineNumber
            MOVE.W SavedLine,-(sp)          * (don't want to corrupt SavedLine)
            MOVE.B SavedXPos,-(sp)
            MOVE.W LineNumber,SavedLine
            MOVE.B ScrollXPos,SavedXPos
            JSR GotoLnVctr
            MOVE.B (sp)+,SavedXPos
            MOVE.W (sp)+,SavedLine
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS
CancGotoLn: MOVE.B (sp)+,MaxLineLen
            MOVE.B #Yes,UseMessage
            JSR FillTosBox                  * Clear messages, et.c....
            MOVE.L #ScrnDsply2,SrcPtr       * (re-show help message)
            JSR PrntStr
            MOVE.B CurntLnYCo,NewCrsrYCo    * Move cursor back to original position...
            MOVE.B D5,NewCrsrXCo
            JSR CrsrMvAc
            RTS


* SUBROUTINE CenterLine. This routine alters the current line of text by replacing it centrally within the line.
*                        If the centering isn't perfect (odd number of non-space characters in line) the screen will flash once.
*                        (if the screen is not flashed, the cursor is moved to the start of the next line) The idea of a screen
*                        flash is so that the user can, if the centered text doesn't look right, just press Undo.

CenterLine: TST.B BuffLength                * Is there anything in the line?
            BNE GoCenterLn                  * Branch if yes.
CenLnCont:  CLR.B CursorXPos                * Otherwise, just move cursor down one.
            JSR CursorDown     
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS
GoCenterLn: MOVE.L TextBuff,SrcPtr          * Take a copy of the current text line...
            MOVE.L #LineWork,DstPtr
            MOVE.B MaxLineLen,LineLen
            JSR CopyData
            MOVE.L TextBuff,Value           * Save address of source line (needed later).
            MOVE.L #LineWork,TextBuff       * Find first character in the line (set parameters for NxtChrRyte...)
            CLR.B Position
            JSR NxtChrRyte
            CLR.L D0
            MOVE.B Position,D0              * Did the position change?
            BEQ NoPosChng                   * Branch if no - no spaces do delete.
            CMP.B BuffLength,D0             * But was a character found?
            BEQ CantCenLn                   * Branch if no.
            MOVE.B BuffLength,LineLen       * Otherwise, delete unwanted spaces...
            SUB.B D0,LineLen
            ADD.L #LineWork,D0
            MOVE.L D0,SrcPtr
            MOVE.L #LineWork,DstPtr
            JSR CopyData
            MOVE.B LineLen,BuffLength       * Set new line length.
NoPosChng:  MOVE.B BuffLength,Position      * Find last character in line...
            JSR NxtChrLeft
            MOVE.B Position,BuffLength      * Set new length of line.
            ADDQ.B #1,BuffLength
            MOVEM.L D4/A4,-(sp)             * SRTBU
            MOVE.B MaxLineLen,D4            * Calculate number of spaces required to center text...
            SUB.B BuffLength,D4
            LSR.B #1,D4
            BEQ CantCenLn2                  * Branch if none needed - line must be full and therefore already centered.
            MOVE.L Value,SrcPtr             * Clear original text line ready for writing...
            MOVE.B MaxLineLen,LineLen
            JSR ClearData
            MOVEA.L Value,A4                * Now insert the required number of spaces. Set pointer to start of line.
            SUBQ.B #1,D4                    * Set counter for number of spaces (1 less for DBRA).
            ANDI.W #$FF,D4                  * Mask out garbage.
PutSpcsLp:  MOVE.B #' ',(A4)+               * Insert the spaces...
            DBRA D4,PutSpcsLp
            MOVE.L A4,DstPtr                * Now copy text to form centered line in memory...
            MOVE.L #LineWork,SrcPtr
            MOVE.B BuffLength,LineLen
            JSR CopyData
            DC.W HideMouse                  * Now update screen...
            JSR CursrOff
            MOVE.L Value,TextBuff           * (set paramters for UpdateLine...)
            MOVE.B CursorYPos,BuffStY
            JSR UpdateLine
            MOVEM.L (sp)+,D4/A4             * RRU
            MOVE.B #Yes,LineChange          * Set flag denoting change.
            BTST.B #0,BuffLength            * Did the text line that's been centered have an odd number of characters in it?
            BNE GoFlshScrn                  * Branch if yes.
            BRA CenLnCont
GoFlshScrn: JSR FlashScrn                   * Otherwise, flash the screen once.
            MOVE.W CurrntLine,LineNumber    * Reset corrupted variables (e.g. BuffLength)...
            JSR GetLineInf
            MOVE.B #No,LineChange           * Clear flag so that Undo buffer isn't changed.
            CLR.B Position                  * Move cursor to first non-space character in line...
            JSR NxtChrRyte
            MOVE.B Position,Column
            MOVE.B Position,SavedXPos
            MOVE.B CursorYPos,Line
            JSR MoveCurs
            RTS
CantCenLn:  MOVE.B #Yes,LineChange          * Set flag denoting change (this will reset any variables that have been altered).
            BRA CenLnCont
CantCenLn2: MOVEM.L (sp)+,D4/A4             * RRU
            BRA CantCenLn


* SUBROUTINE FlashScrn. This routine flashes the screen by inverting it twice.

FlashScrn:  JSR WaitForVS
            JSR Invert
            JSR WaitForVS
            JSR Invert
            RTS


* SUBROUTINE UpHalfPage. This routine scrolls the text up 10 lines.

UpHalfPage: JSR CursrOff
            MOVE.B D4,-(sp)                 * SRTBU
            MOVEQ #8,D4                     * Set counter. Need 10 attempts, therefore set counter to two less (extra for DBRA)...
            JSR ScrollUp
            CMPI.B #No,ScrnChange           * Did the scroll occur?
            BEQ UDHPEnd                     * Branch if no; scrolling ends.
            MOVE.B #Yes,LineChange          * Set line change flag.
UHPLoop:    JSR ScrollUp
            CMPI.B #No,ScrnChange           * Did the scroll occur?
            BEQ UDHPEnd                     * Branch if no; scrolling ends.
            DBRA D4,UHPLoop                 * Otherwise, try again if possible.
UDHPEnd:    MOVE.B (sp)+,D4                 * RRU
HPEnd:      MOVE.B CursorYPos,YCo_ord       * Move cursor to new position...
            JSR FindLineNo
            JSR GetLineAdr
            MOVEA.L D0,A0
            BRA CursCont


* SUBROUTINE DnHalfPage. This routine scrolls the text down 10 lines.

DnHalfPage: JSR CursrOff
            MOVE.B D4,-(sp)                 * SRTBU
            MOVEQ #8,D4                     * Set counter. Need 10 attempts, therefore set counter to two less (extra for DBRA)...
            JSR ScrollDown
            CMPI.B #No,ScrnChange           * Did the scroll occur?
            BEQ UDHPEnd                     * Branch if no; scrolling ends.
            MOVE.B #Yes,LineChange          * Set line change flag.
DHPLoop:    JSR ScrollDown
            CMPI.B #No,ScrnChange           * Did the scroll occur?
            BEQ UDHPEnd                     * Branch if no; scrolling ends.
            DBRA D4,DHPLoop                 * Otherwise, try again if possible.
            BRA UDHPEnd                     * Branch to restore cursor position.


* SUBROUTINE PageUp. This routine moves the text up one page.

PageUp:     MOVE.W NumLines,LineNumber      * Is the bottom line visible?
            JSR IsItVisibl
            CMPI.B #No,D0
            BEQ GoPageUp                    * Branch if no, page shift is go.
            RTS
GoPageUp:   ADDI.W #21,TopLine              * Set new value.
            MOVE.W NumLines,D0              * If this is too much, set so that last line is at bottom of screen...
            SUBI.W #21,D0
            CMP.W TopLine,D0
            BGT TopLineOk                   * (branch if new value o.k.)
            MOVE.W D0,TopLine
TopLineOk:  JSR CursrOff
            JSR ShowText
            BRA HPEnd                       * Jump to identical code.


* SUBROUTINE PageDown. This routine moves the text up one page.

PageDown:   TST.W TopLine                   * Is the top line visible?...
            BNE GoPageDown                  * Branch if no, page shift is go.
            RTS
GoPageDown: SUBI.W #21,TopLine              * Set new value.
            BPL TopLineOk                   * branch if new value o.k.
            CLR.W TopLine                   * Otherwise, set so first line of text is at top of text window area.
            BRA TopLineOk


* SUBROUTINE ToTopLeft. This routine moves the cursor to the top left of the screen.

ToTopLeft:  MOVE.B #4,D0
            BRA ToTpLftVec                  * Jump to identical code.


* SUBROUTINE ToMidLeft. This routine moves the cursor to the middle left of the screen.

ToMidLeft:  MOVE.B #14,YCo_ord
            BRA MidLftVec                   * Jump to identical code.


* SUBROUTINE ToBotmLeft. This routine moves the cursor to the bottom left of the screen.

ToBotmLeft: MOVE.B #24,YCo_ord              * Get line number of line that would be at bottom of screen...
MidLftVec:  JSR FindLineNo
            CMP.W NumLines,D0               * Is the line past the last text line?
            BLE NotTooFar2                  * Branch if no.
            MOVE.W NumLines,D0              * Otherwise, set line to be last line.
NotTooFar2: MOVE.W D0,LineNumber            * Find the y-co ordinate for the line to jump to...
            JSR GetLineYCo
ToTpLftVec: CMP.B CursorYPos,D0             * Is the cursor already there?
            BNE GoToBtmLft                  * Branch if no here...
            TST.B CursorXPos
            BNE GoToLeft                    * ...or here.
            RTS
GoToBtmLft: MOVE.B #Yes,LineChange          * Set flag as cursor is on a new line.
            MOVE.B D0,NewCrsrYCo
GoToLeft:   CLR.B NewCrsrXCo
            CLR.B ScrollXPos
            DC.W HideMouse
            JSR CursrOff
            JSR CrsrMvAc
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS


* SUBROUTINE ToTopOfTxt. This routine moves the cursor to the top line of text.

ToTopOfTxt: CMPI.W #1,CurrntLine            * Is the cursor already on the top line of text?
            BNE TTOTCont                    * Branch if no.
            RTS
TTOTCont:   DC.W HideMouse
            JSR CursrOff
            TST.W TopLine                   * Is the top line visible?
            BEQ TTOTCont2                   * Branch if yes.
            CLR.W TopLine                   * Otherwise, re-show text so that top line is visible...
            JSR ShowText
TTOTCont2:  MOVE.W #1,LineNumber            * Now move cursor to top line...
            JSR GetLineInf
            MOVE.B ScrollXPos,NewCrsrXCo
            MOVE.B #4,NewCrsrYCo
            JSR GetOkXCo
            JSR CrsrMvAc
            MOVE.B #Yes,LineChange          * On a new line, so set flag.
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS


* SUBROUTINE ToBotOfTxt. This routine moves the cursor to the bottom line of text.

ToBotOfTxt: MOVE.W NumLines,D0              * Is the cursor already on the bottom line of text?
            CMP.W CurrntLine,D0
            BNE TBOTCont                    * Branch if no.
            RTS
TBOTCont:   DC.W HideMouse
            JSR CursrOff
            MOVE.W NumLines,LineNumber      * Is the bottom line visible?
            JSR IsItVisibl
            CMPI.B #Yes,D0
            BEQ TBOTCont2                   * Branch if yes.
            MOVE.W NumLines,TopLine
            SUB.W #21,TopLine               * Otherwise, re-show text so that bottom line is visible...
            JSR ShowText
TBOTCont2:  MOVE.W NumLines,LineNumber      * Now move cursor to bottom line...
            JSR GetLineInf
            JSR GetLineYCo
            MOVE.B D0,NewCrsrYCo
            MOVE.B ScrollXPos,NewCrsrXCo    * (set parameters for GetOkXCo...)
            JSR GetOkXCo
            JSR CrsrMvAc
            MOVE.B #Yes,LineChange          * On a new line, so set flag.
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS


* FUNCTION GetOkYCo. This routine returns the x co-ordinate in D0 that the cursor x position should be set to if the cursor was
*                    moved to a text line whose start address is already in D0, taking SavedXPos into account.

GetOkYCo:   MOVEA.L D0,A0                   * Is it possible to set the cursor x-co to the saved position?
            CLR.W D1
            MOVE.B ScrollXPos,D1
            TST.B 0(A0,D1.W)
            BEQ SetToBufLn                  * Branch if no.
            MOVE.B ScrollXPos,D0            * Otherwise, set D0 to saved position.
            RTS
SetToBufLn: MOVE.L A0,SrcPtr                * In that case, just set the cursor to be at the end of the line...
            MOVE.B BuffLength,-(sp)
            JSR FindBufLen
            MOVE.B BuffLength,D0
            MOVE.B (sp)+,BuffLength
            RTS


* SUBROUTINE CursorUp. This routine moves the cursor up one position on the screen. If the cursor is at the top of the screen,
*                      the text is just scrolled down (if the text can't be scrolled, ScrnChange returns No, else Yes).

CursorUp:   MOVE.B #No,ScrnChange           * Assume attempt will fail.
            CMPI.B #4,CursorYPos            * Is the cursor at the top of the screen?
            BNE NotAtTop                    * Branch if no.
            TST.W TopLine                   * Is the cursor at the top of the text?
            BNE GoCursUp                    * Branch if no.
            RTS
GoCursUp:   JSR CursrOff
            JSR ScrollDown                  * Scroll the text and reposition the cursor.
CursUpCont: MOVEA.L TextBuff,A0             * Get start address of new line...
            SUBA.L #81,A0
CursCont:   MOVE.B CursorYPos,NewCrsrYCo
            CLR.W D1                        * Is it possible to set the cursor x-co to the saved position?
            MOVE.B ScrollXPos,D1
            TST.B 0(A0,D1.W)
            BEQ StToBufLen                  * Branch if no.
            MOVE.B ScrollXPos,NewCrsrXCo    * Otherwise, set new cursor x-co to saved position.
            BRA GoCMA
StToBufLen: MOVE.L A0,SrcPtr                * In that case, just set the cursor to be at the end of the line...
            JSR FindBufLen
            MOVE.B BuffLength,NewCrsrXCo
GoCMA:      JSR CrsrMvAc
            MOVE.B #Yes,ScrnChange          * Set flag and exit...
            MOVE.B #Yes,LineChange          * (Set line change flag as well)
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS
NotAtTop:   SUBQ.B #1,CursorYPos            * Move cursor up one line...
            BRA CursUpCont


* SUBROUTINE CursorDown. This routine moves the cursor down one position on the screen. If the cursor is already at the bottom of
*                        the screen, the text is just scrolled up (if the text can't be scrolled, ScrnChange returns No, else Yes)

CursorDown: MOVE.B #No,ScrnChange           * Assume attempt will fail.
            MOVE.B CursorYPos,YCo_ord       * Calculate which physical text line the cursor is on...
            JSR FindLineNo
            CMP.W NumLines,D0               * Is the cursor on the last line of text?
            BNE TestForBot                  * Branch if no.
            RTS
TestForBot: CMPI.B #24,CursorYPos           * Is the cursor at the bottom of the screen?
            BNE NotAtBot                    * Branch if no.
GoCursDn:   JSR CursrOff
            JSR ScrollUp                    * Scroll the text and reposition the cursor.
CursDnCont: MOVEA.L TextBuff,A0             * Get start address of new line...
            ADDA.L #81,A0
            BRA CursCont
NotAtBot:   ADDQ.B #1,CursorYPos            * Move cursor down one line...
            BRA CursDnCont


* FUNCTION QScrllXPos. This function decides whether, given the number of a physical text line currently on screen, it is possible
*                      to set the cursor x position to the currently saved position in ScrollXPos.
*                      The line number must be in D0 when the function is called.
*                      D0 returns the answer - either Yes or No.

QScrllXPos: JSR GetLineAdr                  * First get address of the line number in question.
            CLR.W D1                        * Is there a zero byte at position ScrollXPos in memory? (If yes, function answer = No)
            MOVE.B ScrollXPos,D1
            MOVEA.L D0,A0
            MOVE.B #No,D0                   * (assume answer is no)
            TST.B 0(A0,D1.W)                * Well? Is there a zero byte?
            BEQ QSXPSEnd                    * Branch if yes, flag is o.k..
            MOVE.B #Yes,D0                  * Otherwise, set flag.
QSXPSEnd:   RTS


* SUBROUTINE CursorLeft. This routine moves the cursor left one position. Any scrolling needed (if the cursor is at the start of a
*                        line) is taken care of.

CursorLeft: TST.B CursorXPos                * Is the cursor at the start of the line?
            BEQ CLContinue                  * Branch if yes.
            MOVE.B CursorXPos,NewCrsrXCo    * Otherwise, just move the cursor left one position...
            SUBQ.B #1,NewCrsrXCo
            BRA CrsLRSet
CLContinue: MOVE.B CursorYPos,YCo_ord       * Is the cursor on the top line of text?
            JSR FindLineNo
            CMPI.W #1,D0
            BNE CLCarryOn                   * Branch if no.
            RTS
CLCarryOn:  MOVE.B CursorYPos,YCo_ord       * Otherwise, the cursor goes up one line. Is that line visible, though?...
            SUBQ.B #1,CursorYPos
            JSR FindLineNo
            SUBQ.W #1,D0
            MOVE.W D0,LineNumber
            JSR IsItVisibl
            CMPI.B #Yes,D0
            BEQ GoCrsLft                    * Branch if yes
            JSR ScrollDown                  * Otherwise, bring the line into view.
            ADDQ.B #1,CursorYPos            * (reset y-co to what it originally was)
GoCrsLft:   JSR GetLineInf                  * Now set cursor to to the length of the line (i.e. the end of the line)...
            MOVE.B BuffLength,NewCrsrXCo
CrsLRSet:   MOVE.B CursorYPos,NewCrsrYCo
            JSR CursrOff
            JSR CrsrMvAc
            MOVE.B #Yes,LineChange          * Set line change flag.
            MOVE.B #Yes,RemNewXPos          * Set flag to reset remembered x position.
            MOVE.B #No,NewLnTaken           * Clear flag.
            RTS
 

* SUBROUTINE CursorRyte. This routine moves the cursor right one position. Any scrolling needed (if the cursor is at the end of a
*                        line) is taken care of.

CursorRyte: MOVE.B CursorYPos,YCo_ord       * Get length of current line...
            JSR FindLineNo
            MOVE.W D0,LineNumber
            JSR GetLineInf
            MOVE.B CursorXPos,D0            * Is the cursor at the end of the line?
            CMP.B BuffLength,D0
            BEQ CRContinue                  * Branch if yes here...
            CMP.B MaxCrsrXCo,D0
            BEQ CRContinue                  * ...or here.
            MOVE.B CursorXPos,NewCrsrXCo    * Otherwise, just move the cursor right one position...
            ADDQ.B #1,NewCrsrXCo
            BRA CrsLRSet
CRContinue: MOVE.W NumLines,D0              * Is the cursor on the bottom line of text?
            CMP.W LineNumber,D0
            BNE CRCarryOn                   * Branch if no.
            RTS
CRCarryOn:  ADDQ.B #1,CursorYPos            * Otherwise, the cursor goes down one line. Is that line visible, though?...
            ADDQ.W #1,LineNumber
            JSR IsItVisibl
            CMPI.B #Yes,D0
            BEQ GoCrsRyte                   * Branch if yes
            JSR ScrollUp                    * Otherwise, bring the line into view.
            SUBQ.B #1,CursorYPos            * (reset y-co to what it originally was)
GoCrsRyte:  CLR.B NewCrsrXCo
            BRA CrsLRSet
 

* SUBROUTINE TextUp. This routine scrolls the text up one line (I know ScrollUp does this, but ScrollUp doesn't take care of
*                    the cursor).

TextUp:     JSR ScrollUp                    * Try and scroll the text up.
            BRA HPEnd


* SUBROUTINE TextDown. This routine scrolls the text down one line (I know ScrollDown does this, but ScrollDown doesn't take care
*                      of the cursor).

TextDown:   JSR ScrollDown                  * Try to move text down one line.
            BRA HPEnd                       * Jump to restore cursor position.


* SUBROUTINE Pause.    This routine waits a bit! (done by waiting for a few vertical screen returns to occur)

Pause:      JSR WaitForVS
            JSR WaitForVS
            JSR WaitForVS
            JSR WaitForVS
            RTS
WaitForVS:  MOVE.W #Vsync,-(sp)             * Code for XBIOS Vsync.
            TRAP #XBios
            ADDQ.W #2,sp                    * CS
            RTS
Pause2:     MOVE.L D5,-(sp)                 * This routine is necessary as using Pause several times for mouse cursor movement
            MOVE.W #$8000,D5                * has very wierd on-screen effects for no apparent reason. A simple loop is used.
PauseLoop:  DBRA D5,PauseLoop
            MOVE.L (sp)+,D5
            RTS


* SUBROUTINE LineToScrn. This routine displays on the screen the text line denoted by LineNumber. No check is made to see whether
*                        LineNumber is valid!

LineToScrn: MOVEQ #0,D0                     * Get y-co of where the line is on screen...
            MOVEQ #0,D1
            JSR GetLineYCo
            MOVE.B D0,D1                    * Multiply by 1280 to get equivalent offset from start of screen memory...
            LSL.W #2,D0                     * (equivalent to D0 = ((D0 * 2) + D0) * 256)
            ADD.W D1,D0
            LSL.W #8,D0
            MOVE.L ScreenStrt,D2            * D2 will point to the next character column in the screen ram to write to.
            ADD.L D0,D2                     * D2 now points to the start of the appropriate screen ram line (D2 is copied to A3
            MOVEA.L CharTblPtr,A0           * during each loop iteration). A0 will point to the character data look up table.
            MOVE.W LineNumber,D0            * Get start address of text line...
            JSR GetLineAdr
            MOVEA.L D0,A1                   * A1 will point to the current position in the text line.
            MOVEQ #0,D0                     * D0 to be used as a counter. Set to MaxLineLen - 1 for DBRA loop...
            TST.B (A1)                      * (but is the line empty?)
            BNE LTSCont                     * (Branch if no)
            MOVEA.L D2,A0                   * (otherwise, clear the line on screen...)
            MOVE.W #39,D1                   * (ram cleared in blocks of 8 longs for speed. Number of blocks = 40 (minus 1 for DBRA).
LTSClearLp: MOVE.L D0,(A0)+                 * (Clear next block...)
            MOVE.L D0,(A0)+
            MOVE.L D0,(A0)+
            MOVE.L D0,(A0)+
            MOVE.L D0,(A0)+
            MOVE.L D0,(A0)+
            MOVE.L D0,(A0)+
            MOVE.L D0,(A0)+
            DBRA D1,LTSClearLp              * (branch back if not finished)
            RTS
LTSCont:    MOVE.B MaxLineLen,D0
            SUBQ.B #1,D0
            MOVE.L A3,-(sp)                 * SRTBU
LTSLoop:    MOVEQ #0,D1                     * D1 will be used as a pointer offset to access the character look up table.
            MOVE.B (A1)+,D1                 * Get next character.
            LSL.W #4,D1                     * Multiply by 16 to get offset in look up table.
            LEA 0(A0,D1.W),A2               * Get start address of data for the character in A2.
            MOVEA.L D2,A3                   * Get copy of start address of current character column position in screen ram.
            ADDQ.L #1,D2                    * Increase ram column position pointer to point to next column.
            MOVE.B (A2)+,(A3)               * Copy the 16 bytes of data to the appropriate ram positions...
            MOVE.B (A2)+,80(A3)
            MOVE.B (A2)+,160(A3)
            MOVE.B (A2)+,240(A3)
            MOVE.B (A2)+,320(A3)
            MOVE.B (A2)+,400(A3)
            MOVE.B (A2)+,480(A3)
            MOVE.B (A2)+,560(A3)
            MOVE.B (A2)+,640(A3)
            MOVE.B (A2)+,720(A3)
            MOVE.B (A2)+,800(A3)
            MOVE.B (A2)+,880(A3)
            MOVE.B (A2)+,960(A3)
            MOVE.B (A2)+,1040(A3)
            MOVE.B (A2)+,1120(A3)
            MOVE.B (A2),1200(A3)
            DBRA D0,LTSLoop                 * Branch back if not finished.
            MOVE.L (sp)+,A3                 * RRU
            RTS


* SUBROUTINE DelScrnLn. This routine removes a screen line based on a given line number. The text below is scrolled up one line,
*                       leaving a blank line at the bottom of the screen.
*                       LineNumber must contain the physical line number to be removed from the screen (memory not affected).

DelScrnLn:  JSR GetLineYCo                  * Get y co-ordinate of required line.
            MOVE.B D0,Line                  * Move cursor to that line...
            CLR.B Column
            JSR MoveCurs
            JSR RemoveLine                  * ... and remove that screen line.
            RTS


* SUBROUTINE InsScrnLn. This routine inserts a blank screen line based on a given line number. The text at the line is scrolled
*                       down one line, leaving a blank line at the line above the scrolled text.
*                       LineNumber must contain the physical line number at which to insert a blank line on the screen (not
*                       memory!).

InsScrnLn:  JSR GetLineYCo                  * Get y co-ordinate of required line.
            MOVE.B D0,Line                  * Move cursor to that line...
            CLR.B Column
            JSR MoveCurs
            JSR InsertLine                  * ...and insert a blank line there.
            RTS


* SUBROUTINE  ShowText. This routine displays what is currently visible of the user's text file in the TWA.

ShowText:   MOVE.W D5,-(sp)                 * SRTBU
            MOVE.W TopLine,LineNumber       * First physical line to be shown is TopLine + 1.
            MOVE.W #20,D5                   * 21 lines to show, so set to 1 less for DBRA.
ShwTxtLoop: ADDQ.W #1,LineNumber            * Get line no. of next line and display it...
            JSR LineToScrn
            DBRA D5,ShwTxtLoop              * Branch back if not finished.
            MOVE.W (sp)+,D5                 * RRU
            RTS


* SUBROUTINE FillWindow. This routine replaces the text on the screen that was/could have been wiped out by various messages.

FillWindow: CMPI.W #6,NumLines              * Are there more than 6 lines?
            BGT GoFillWndw                  * Branch if yes.
            JSR ClearWndow                  * Otherwise, just clear unwanted muck.
            RTS
GoFillWndw: MOVE.W D4,-(sp)                 * SRTBU
            MOVE.W TopLine,LineNumber       * Calculate first physical text line to be shown (TopLine + 7)...
            ADDQ.W #7,LineNumber
            MOVEQ.W #8,D4                   * 9 lines to show, so set to 1 less for DBRA.
ShowMore:   MOVE.B #Yes,ClrOldChrs          * Set flag for ShowLine.
            JSR LineToScrn
            ADDQ.W #1,LineNumber
            DBRA D4,ShowMore                * Branch back if not finished.
            MOVE.W (sp)+,D4                 * RRU
            RTS


* SUBROUTINE FillTosBox. This routine's purpose is to replace the text on the screen deleted by TosError operations.
*                        It works in an almost identical way to FillWindow.

FillTosBox: CMPI.W #7,NumLines              * Are there more than 7 lines?
            BGT GoFTB                       * Branch if yes.
            JSR DelTosBox                   * Otherwise, all that needs to be done is for the tos error box area to be cleared.
            RTS
GoFTB:      MOVE.W D4,-(sp)                 * SRTBU
            MOVE.W TopLine,LineNumber       * Calculate first physical text line to be shown (TopLine + 8)...
            ADDQ.W #8,LineNumber
            MOVEQ #4,D4                     * 5 lines to show, so set to 1 less for DBRA.
            BRA ShowMore                    * Rest of code is identical to the last part of FillWindow.


* SUBROUTINE ScrollDown. This routine scrolls the text on the screen down one line, if possible.
*                        If the scroll occured, ScrnChange returns Yes, otherwise No.

ScrollDown: MOVE.B #Yes,ScrnChange          * Assume scroll will occur.
            TST.W TopLine                   * But is the first line in the file already visible?
            BNE GoScrllDn                   * Branch if no - scroll is allowed.
            MOVE.B #No,ScrnChange           * Otherwise, clear flag and exit...
            RTS
GoScrllDn:  JSR CursrOff                    * Copy the upper 20 lines of the TWA down one line...
            LEA 30720,A0
            ADDA.L ScreenStrt,A0
            LEA 32000,A1
            ADDA.L ScreenStrt,A1
            MOVE.W #799,D0                  * 800 blocks of 8 long words to copy.
SDLoop:     MOVE.L -(A0),-(A1)              * Copy next block...
            MOVE.L -(A0),-(A1)
            MOVE.L -(A0),-(A1)
            MOVE.L -(A0),-(A1)
            MOVE.L -(A0),-(A1)
            MOVE.L -(A0),-(A1)
            MOVE.L -(A0),-(A1)
            MOVE.L -(A0),-(A1)
            DBRA D0,SDLoop
            MOVE.W TopLine,LineNumber       * Display new line at top of text window area...
            SUBQ.W #1,TopLine               * Set TopLine to new value (must be done before LineToScrn call).
            JSR LineToScrn
            RTS                        


* SUBROUTINE ScrollUp. This routine scrolls the text on the screen up one line, if possible.
*                      If the scroll occured, ScrnChange returns Yes, otherwise No.

ScrollUp:   MOVE.B #Yes,ScrnChange          * Assume scroll will occur.
            MOVE.W TopLine,D0               * If TopLine + 21 < NumLines, then scroll is permitted. Well, is it permitted?
            ADD.W #21,D0
            CMP.W NumLines,D0
            BLT GoScrollUp                  * Branch if yes.
            MOVE.B #No,ScrnChange           * Otherwise, clear flag and exit...
            RTS
GoScrollUp: JSR CursrOff                    * Copy the bottom 20 lines of screen ram up 1 line...
            LEA 5120,A0
            ADDA.L ScreenStrt,A0
            LEA 6400,A1
            ADDA.L ScreenStrt,A1
            MOVE.W #799,D0                  * 800 blocks of 8 long words to copy.
SULoop:     MOVE.L (A1)+,(A0)+              * Copy next block...
            MOVE.L (A1)+,(A0)+
            MOVE.L (A1)+,(A0)+
            MOVE.L (A1)+,(A0)+
            MOVE.L (A1)+,(A0)+
            MOVE.L (A1)+,(A0)+
            MOVE.L (A1)+,(A0)+
            MOVE.L (A1)+,(A0)+
            DBRA D0,SULoop                  * Branch back if not finished.
            ADDQ.W #1,TopLine               * Increase TopLine.
            MOVE.W TopLine,LineNumber       * Display new line at bottom of TWA...
            ADDI.W #21,LineNumber
            JSR LineToScrn
            RTS


* SUBROUTINE MakeWindow. This routine sets up a clear area with a border at top and bottom in the centre 7 lines of the screen for
*                        message display purposes.
 
MakeWindow: JSR ClearWndow                  * First clear the required area.
            MOVE.L #WindowBox,SrcPtr        * Set pointer to start of window string.
            JSR PrntStr                     * Window drawn.
            RTS


* SUBROUTINE ClearWndow. This routine wipes the middle 7 lines of the screen.

ClearWndow: MOVE.L #ClearWindw,SrcPtr       * Set address of clear window command string ready for PrntStr.
            JSR PrntStr                     * Window is now clear.
            RTS


* SUBROUTINE GetDskTrAd. This routine gets the Disk Transfer Address. It is stored in DskTrnsAdd.

GetDskTrAd: MOVE.W #FGetDTA,-(sp)           * Function number for GEMDOS FGetDTA.
            TRAP #GemDos
            ADDQ.L #2,sp                    * CS
            MOVE.L D0,DskTrnsAdd            * Save result.
            RTS


* SUBROUTINE GetDskFSpc. This routine returns the amount of free space on the drive specified by CurrentDrv (which should contain
*                        the ASCII code for the drive required - 65 for drive A, 66 for B, 67 for C and so on).
*                        The number of free bytes is returned in DskFreeSpc.
*                        The routine is not used by this version of the Sorcerer, but will be used in later versions and so has
*                        been written now for convenience.

GetDskFSpc: CLR.W D0                        * Drive specifier: Drive A = 1, B = 2, e.t.c. so convert ASCII letter in CurrentDrv...
            MOVE.B CurrentDrv,D0            * Value = ASCII letter code - (ASCII code for 'A' minus one = ASCII code for '@').
            SUB.B #'@',D0
            MOVE.W D0,-(sp)                 * Code now on stack.
            MOVE.L #FrAlcUnits,-(sp)        * Address of Disk Free Space Buffer on stack.
            MOVE.W #DFree,-(sp)             * Function number for GEMDOS DFree.
            TRAP #GemDos
            ADDA.W #8,sp                    * CS
            MOVE.L BytsPrSctr,D0            * Calculate number of free bytes on specified drive...
            MULU.W PhysSecAlc,D0            * Disk Free Space = BytsPrSctr * PhysSecAlc * FrAlcUnits...
            MULU.W FrAlcUnits,D0           
            MOVE.L D0,DskFreeSpc            * Save Result.
            RTS


* SUBROUTINE SplitPFNam. This routine splits the path/file name pointed to by SrcPtr into the drive, path and file.
*                        Results of routine: CurrentDrv contains the drive letter,
*                                            PathName contains the path specification (up to, and including, the last '\' symbol),
*                                            FileName contains the file name.

SplitPFNam: MOVEM.L D4-D5/A4,-(sp)          * SRTBU
            MOVEA.L SrcPtr,A4               * Take copy of SrcPtr contents and save (SrcPtr will be changed for Clear/CopyData)...
            MOVE.L A4,-(sp)
            MOVE.B (A4),CurrentDrv          * Save drive letter.
            MOVE.L #PathName,SrcPtr         * Clear PathName and FileName of old garbage...
            MOVE.B #PathBufLen,LineLen
            JSR ClearData
            MOVE.L #FileName,SrcPtr
            MOVE.B #FileBufLen,LineLen
            JSR ClearData
            CLR.L D4                        * Now find end of path name, i.e. find position of last '\' symbol...
            MOVE.L (sp),SrcPtr              * Find length of path/file name to be scanned...
            JSR FindBufLen
            MOVE.B BuffLength,D4
            ADDA.L D4,A4                    * A4 now points to after last character in path/file name.
FindBSlash: CMPI.B #'\',-(A4)               * Found the slash yet?
            BNE FindBSlash                  * Branch if  no.
            MOVE.L A4,D5                    * Find length of path name (from start up to, & including, the last '\' symbol)...
            SUB.L (sp),D5
            ADDQ.B #1,D5
            MOVE.B D5,LineLen               * Now copy path name part of path/file name into PathName buffer.
            MOVE.L (sp),SrcPtr
            MOVE.L #PathName,DstPtr
            JSR CopyData
            SUB.B  D5,D4                    * Find length of file name (from after last '\' up to last character, inclusive)...
            MOVE.B D4,LineLen               * Now copy file name to FileName buffer.
            ADDQ.W #1,A4
            MOVE.L A4,SrcPtr
            MOVE.L #FileName,DstPtr
            JSR CopyData
            MOVE.L (sp)+,SrcPtr             * Restore SrcPtr.
            MOVEM.L (sp)+,D4-D5/A4          * RRU
            RTS


* SUBROUTINE SetDirPath. This routine sets the current directory path name. SrcPtr must point to the start of the string.
*                        Result is in D0 (-ve indicates error).

SetDirPath: MOVE.L #PathName,-(sp)          * Try to set path definition stored at PathName...
            MOVE.W #DSetpath,-(sp)          * Function number for GEMDOS DSetpath.
            TRAP #GemDos
            ADDQ.L #6,sp                    * CS
            RTS

* SUBROUTINE CheckPFNam. This routine scans a given path/file name and determines whether the P/F name is valid or not.
*                        SrcPtr must point to the start of the P/F name,
*                        BuffLength must contain the number of characters in the P/F name,
*                        PathScan must contain Yes if just a path name is being validated, No otherwise.
*                        If the P/F name is okay, ErrorCode returns zero; else one of the following is returned in ErrorCode:-

*         ErrorCode                           Definition.

*           -127                Drive identifier invalid or drive not accessible.
*           -126                Invalid drive specification. Must be <drv>:\[<path\]<file name>
*           -125                Path/Directory/File name invalid or missing.
*           -124                Path description invalid/does not exist or last '\' symbol missing.

CheckPFNam: MOVEM.L D4-D5/A3-A5,-(sp)       * SRTBU
            CLR.B ErrorCode                 * Initialise error code.
            MOVE.L SrcPtr,A4                * Transfer address of start of PF name for general use.
            MOVE.L A4,-(sp)
            CLR.L D5                        * Transfer BuffLength for general use...
            MOVE.B BuffLength,D5
            BEQ PthFlDirEr                  * Branch if nothing in the line.
            MOVEA.L A4,A0                   * Now convert any lower case letters to upper case...
            MOVEA.L A4,A5                   * (set loop start and end addressess)
            ADD.W D5,A5
NoLwrCase:  CMPA.L A0,A5                    * Any more characters?
            BEQ LwrCaseGon                  * Branch if no.
            MOVE.B (A0)+,D1                 * Get next character.
            CMPI.B #'a',D1                  * If not a lower case letter, branch back to scan next character...
            BLT NoLwrCase
            CMPI.B #'z',D1
            BGT NoLwrCase
            SUB.B #'a'-'A',-1(A0)           * Otherwise, convert to upper case and scan next character...
            BRA NoLwrCase
LwrCaseGon: MOVE.B (A4)+,D4                 * Now for validation: Get supposed drive identifier...
            CMPI.B #'A',D4                  * Branch out if drive identifier NOT IN ['A'..'P']...
            BLT DriveIdErr                  * (here)
            CMPI.B #'P',D4
            BGT DriveIdErr                  * (or here)
            MOVE.W #Drvmap,-(sp)            * Capital letter may be in range, but is it a connected drive?...
            TRAP #Bios                      * Bios call Drvmap gives a bit map in D0: bit k set if drive k connected (A=0,e.t.c.)
            ADDQ.L #2,sp                    * CS
            SUB.B #'A',D4                   * Convert drive letter to a bit vector (A = 0, B = 1, C = 2, e.t.c.)
            BTST.L D4,D0                    * Is this drive connected? (bit cleared if no)
            BEQ DriveIdErr                  * Branch if no.
            CMPI.B #':',(A4)+               * The next 2 characters must be ':\'.
            BNE ErrDrvSpec                  * Branch if 1st one not present.
            CMPI.B #'\',(A4)+
            BNE ErrDrvSpec                  * Branch if 2nd one not present.
            CMPI.B #No,PathScan             * Now, is this just a path-name-only scan?
            BEQ BeginCheck                  * Branch if no.
            CMPI.B #'\',-1(A5)              * Otherwise, the last character must be a '\'...
            BEQ BeginCheck                  * Branch if it is.
            BRA PathErr                     * Otherwise, jump to error set.
BeginCheck: MOVEA.L A4,A3                   * Now run validation on each directory/file name...
FindBlkEnd: CMPA.L A4,A5                    * First find start and end of name (start in A3, end in A4)
            BEQ ScanName                    * Branch if end of line reached.
            CMPI.B #'\',(A4)+               * Reached the next '\' yet?
            BNE FindBlkEnd                  * Branch if no.
            SUBQ.L #1,A4                    * Otherwise, correct to point to the '\' symbol.
ScanName:   MOVE.L A4,D1                    * Calculate number of characters in this name (result in D1)
            SUB.L A3,D1
            CMPI.B #No,PathScan             * Is this a path-name-only scan?
            BEQ FullPFScan                  * Branch if no.
            TST.B D1                        * Otherwise, if this name is zero length...
            BEQ PFSpecOk                    * ...branch out, since end of path name reached and all is o.k..
FullPFScan: TST.B D1                        * If scanning a full path/file name length must not be zero. Is the line empty?
            BEQ PthFlDirEr                  * Branch if yes.
            MOVE.B #No,Found                * Okay, now search for a full stop within the directory/file name...
            MOVEA.L A3,A2                   * Will use A2 to search.
FullStopLp: CMPA.L A2,A4                    * Reached end of name?
            BEQ FSLoopEnd                   * Branch if yes.
            CMPI.B #'.',(A2)+               * Otherwise, is there a full stop there?
            BNE FullStopLp                  * Branch back and look at next character if no.
            SUBQ.L #1,A2                    * Otherwise, full stop found - correct A2 to point to this and set flag...
            MOVE.B #Yes,Found
FSLoopEnd:  MOVE.L A2,D1                    * Find number of characters between where A2 stopped scanning and start of name.
            SUB.L A3,D1                     * Result in D1 (Assert: Byte value in D1 >0 since zero case already checked above).
            CMPI.B #8,D1                    * Is the file name more than 8 characters?
            BGT PthFlDirEr                  * Branch if yes, as more than 8 is to long.
            CMPI.B #No,Found                * Otherwise, was a full stop discovered?
            BEQ SkipFSChk                   * Branch if no.
            MOVE.L A4,D1                    * Otherwise, find no. of char's between name end (where A4 points to) & full stop...
            SUB.L A2,D1                     * (It is the number of characters between A4 and the full stop (at A2) that is wanted,
            SUBQ.B #1,D1                    * but A2 points to the full stop, so take off an extra one to exclude the full stop)
            BEQ PthFlDirEr                  * Branch if extension length is zero (not o.k.).
            CMPI.B #3,D1                    * Is the extension more than 3 characters long?
            BLE SkipFSChk                   * Branch if no, as it's o.k..
            BRA PthFlDirEr                  * Otherwise, jump to error set.
SkipFSChk:  CMPA.L A4,A5                    * Anything more to scan?
            BEQ PFSpecOk                    * Branch if no.
            ADDQ.L #1,A4                    * Otherwise, move pointer to start of next directory/file name (if there is one)...
            BRA BeginCheck                  * ...and start the next directory/name validation scan!

DriveIdErr: MOVE.B #DrvIdError,ErrorCode    * Set error & exit.
            BRA CheckPFEnd
ErrDrvSpec: MOVE.B #DrvSpecErr,ErrorCode    * Set error & exit.
            BRA CheckPFEnd
PthFlDirEr: MOVE.B #PDFError,ErrorCode      * Set error & exit.
            BRA CheckPFEnd
PathErr:    MOVE.B #PathError,ErrorCode     * Set error & exit.
            BRA CheckPFEnd
 
PFSpecOk:   JSR SplitPFNam                  * Split path/file name up into drive, path & file.
            JSR SetDirPath                  * Path name specification seems valid, but...
            TST.W D0                        * ...does the path exist?
            BPL PathExists                  * Branch if yes.
            MOVE.L #OldPFName,SrcPtr        * Otherwise, reset the old valid path name and jump to set error...
            JSR SplitPFNam
            JSR SetDirPath
            BRA PathErr
PathExists: MOVE.L #OldPFName,SrcPtr        * Reset the old valid path name...
            JSR FindBufLen
            JSR SplitPFNam
            JSR SetDirPath
CheckPFEnd: MOVE.L (sp)+,SrcPtr             * Pull original SrcPtr back.
CheckFin:   MOVEM.L (sp)+,D4-D5/A3-A5       * RRU
            RTS


* SUBROUTINE BuildPFNam. This routine builds the complete path/file name in OldPFName. The three components (drive spec., path
*                        spec. and file spec.) are drawn from the PathName (already contains the drive spec.) & FileName buffers.
*                        The original path/file names aren't changed. BuffLength returns the final size of the OldPFName buffer.
*                        Note: Once the OldPFName buffer is fully constructed, a copy of this is placed in the PthFileNam buffer.

BuildPFNam: MOVE.L #OldPFName,SrcPtr        * First clear OldPFName of any previous garbage...
            MOVE.B #80,LineLen
            JSR ClearData
            MOVE.L SrcPtr,DstPtr            * Copy path name to OldPFName...
            MOVE.L #PathName,SrcPtr
            JSR FindBufLen
            MOVE.B BuffLength,LineLen
            JSR CopyData
            CLR.L D0                        * Now point DstPtr to after path name and copy over file name.
            MOVE.B BuffLength,D0
            ADD.L D0,DstPtr
            MOVE.L #FileName,SrcPtr
            JSR FindBufLen
            MOVE.B BuffLength,LineLen
            JSR CopyData
            MOVE.L #OldPFName,SrcPtr       * Finally find final length of path/file name buffer and take a copy to PthFileNam
            JSR FindBufLen                 * for outside use. SaveFileAs uses both buffers, for instance.
            MOVE.L #PthFileNam,DstPtr
            MOVE.B #80,LineLen             * (copying entire buffer, including blank part, ensures deletion of old garbage)
            JSR CopyData
            RTS


* SUBROUTINE SeekFile. This routine finds whether a specified file is present via the path/file name pointed to by SrcPtr.
*                      If the file is found, ErrorCode contains zero - otherwise ErrorCode contains the appropriate error number
*                      (usually -33 for 'file not found').

SeekFile:   MOVE.B #Seeking,FileOpMode      * Set file operation mode in case of unwanted error.
            CLR.W -(sp)                     * Now search for file (0 on stack for normal R/W file)...
            MOVE.L SrcPtr,-(sp)             * Address of path/file name.
            MOVE.W #SFirst,-(sp)            * Function number for GEMDOS SFirst.
            TRAP #GemDos
            ADDA.W #8,sp                    * CS
            MOVE.B D0,ErrorCode             * Save result.
            RTS


* SUBROUTINE CreateFile. This routine creates a file whose path/file name is pointed to by SrcPtr.
*                        If all goes well, the file handle number is placed in FileHandle and ErrorCode contains 0; otherwise,
*                        ErrorCode contains the -ve error number and FileHandle contains 0.

CreateFile: MOVE.B #Creating,FileOpMode     * Set file operation mode in case of error.
            CLR.B ErrorCode                 * Initialise result variables.
            CLR.W FileHandle
            CLR.W -(sp)                     * Now create file (pLace a 0 on stack for normal R/W file status)...
            MOVE.L SrcPtr,-(sp)             * Address of path/file name.
            MOVE.W #FCreate,-(sp)           * Function number for GEMDOS FCreate.
GoFileActn: TRAP #GemDos                    * (N.B.! OpenFile jumps to here to avoid code repetition)
            ADDA.W #8,sp                    * CS
            TST.B D0                        * Any errors?
            BPL NoFileErr                   * Branch if no.
            MOVE.B D0,ErrorCode             * Otherwise, set error code & exit...
            RTS
NoFileErr:  MOVE.W D0,FileHandle            * Save file handle & exit.
            RTS


* SUBROUTINE OpenFile. This routine opens a file, whose path/file name is pointed to by SrcPtr. Same result given via ErrorCode &
*                      FileHandle as for CreateFile. A normal read/write file is assumed.

OpenFile:   MOVE.B #Opening,FileOpMode      * Set file operation mode in case of error.
            CLR.B ErrorCode                 * Initialise result variables...
            CLR.W FileHandle
            MOVE.W #2,-(sp)                 * Now open file (code 2 on stack for normal R/W status)...
            MOVE.L SrcPtr,-(sp)             * Address of path/file name.
            MOVE.W #FOpen,-(sp)             * Function number for GEMDOS FOpen.
            BRA GoFileActn                  * Now branch to execute the FOpen routine (code required already in CreateFile)...

         
* SUBROUTINE ReadBlock. This routine reads in a block of data from a disk file. The file must already be open. Its file handle must
*                       be in FileHandle. BlockSize (long word) must contain the number of bytes to load. The block of data will
*                       always be loaded into memory beginning at WorkSpace.
*                       If an error occurs, ErrorCode contains the error code.
*                       Note! FileOpMode must be set to #Loading in the calling routine as doing it here would slow things down.

ReadBlock:  CLR.B ErrorCode                 * Initialise error code.
            MOVE.B #Loading,FileOpMode      * Set file operation mode in case of error.
            MOVE.L #WorkSpace,-(sp)         * Address of where the data is to be loaded to.
            MOVE.L BlockSize,-(sp)          * Number of bytes to read.
            MOVE.W FileHandle,-(sp)
            MOVE.W #FRead,-(sp)             * Function number for GEMDOS FRead.
            TRAP #GemDos
            ADDA.W #12,sp                   * CS
            TST.W D0                        * Any errors?
            BPL BlokReadOk                  * Branch if no.
            MOVE.B D0,ErrorCode             * Otherwise, set error code.
BlokReadOk: RTS


* SUBROUTINE ReadByte. This routine reads one byte from a disk file. Similar parameters to ReadBlock, except byte is stored
*                      at the location pointed to by A5.


ReadByte:   CLR.B ErrorCode                 * Initialise error code.
            MOVE.L A5,-(sp)                 * Address of where the byte is to be sent.
            MOVE.L #1,-(sp)                 * Number of bytes to read.
            MOVE.W FileHandle,-(sp)
            MOVE.W #FRead,-(sp)             * Function number for GEMDOS FRead.
            TRAP #GemDos
            ADDA.W #12,sp                   * CS
            TST.B D0                        * Any errors?
            BPL ByteReadOk                  * Branch if no.
            MOVE.B D0,ErrorCode             * Otherwise, set error code.
ByteReadOk: RTS


* SUBROUTINE WriteData. This routine sends a specified number of bytes to a disk file (whose handle number is in FileHandle). The
*                       number of bytes to send must be in LineLen. SrcPtr must point to the first byte to send. If an error
*                       occurs, ErrorCode contains the error number - otherwise ErrorCode will contain 0.
*                       Note! FileOpMode must be set to #Writing in the calling routine as doing it here would slow things down.

WriteData:  CLR.B ErrorCode                 * Initialise error code.
            MOVE.L SrcPtr,-(sp)             * Address of the source data buffer.
            CLR.L -(sp)                     * Reserve space for number of bytes to send.
            MOVE.B LineLen,3(sp)            * Number of bytes to send less than 256. Usually will be <= MaxLineLen.
            MOVE.W FileHandle,-(sp)
            MOVE.W #FWrite,-(sp)            * Function number for GEMDOS FWrite.
            TRAP #GemDos
            ADD.L #12,sp                    * CS
            TST.L D0                        * Any errors?
            BPL NoWrtError                  * Branch if no.
            MOVE.B D0,ErrorCode             * Otherwise, save error number.
NoWrtError: RTS


* SUBROUTINE CloseFile. This routine closes the specified file whose handle number is in FileHandle. If an error occurs, ErrorCode
*                       contains the error number, otherwise ErrorCode contains 0.

CloseFile:  MOVE.B #Closing,FileOpMode      * Set file operation mode in case of error.
            MOVE.W FileHandle,-(sp)         * Handle number of file to close.
            MOVE.W #FClose,-(sp)            * Function number of GEMDOS FClose.
            TRAP #GemDos
            ADDQ.W #4,sp                    * CS
            MOVE.B D0,ErrorCode             * Save error number (zero if no error).
            RTS
   

* SUBROUTINE GetErrAddr. This routine finds the address of the error string message based on the cvontents of ErrorCode.
*                        The address is returned in SrcPtr. For TosError use, the absolute error code value is returned in Value.

GetErrAddr: CLR.L D0                        * (transfer error code)
            MOVE.B ErrorCode,D0
            NEG.B D0                        * (need a positive value for Convert/finding address of error string)
            MOVE.L D0,Value                 * (Save value ready for Convert, since TosError may be calling this routine.)
            LEA ErrCodeLst,A0               * Now find address of error string message which matches code number.
            LSL.W #1,D0
            MOVEA.W 0(A0,D0.W),A5
            ADDA.L #ErrAddrLst,A5
            MOVE.L (A5),SrcPtr              * Store result & exit.
            RTS
            
            
* SUBROUTINE DelTosBox. This routine deletes the area on the screen where the TosError box is (going to be).

DelTosBox:  MOVE.L #ClrTosBox,SrcPtr        * Set SrcPtr to address of ASCII control sequence (ready for PrntStr).
            JSR PrntStr
            RTS


* SUBROUTINE TosError. This routine displays what has gone wrong with the disk file accessing and asks the user what is to be
*                      done next. NB! The following is what the Tos error message box looks like (example)...
*                      
*  ----------------------------------------------------------------------------
* | Tos error -32 whilst seeking file.   What now?       Alter     Cancel      |
* | (File not found)                                   path/file               |
* | What now?  (Click on appropriate box)                name      Retry       |
*  ----------------------------------------------------------------------------

TosError:   DC.W HideMouse                  * Screen is changing, so hide mouse.
            JSR DelTosBox                   * Display message box on screen saying what has gone wrong (first, clear area needed).
            MOVE.L #TosErrBox,SrcPtr        * Display as much of the box in one go as possible!
            JSR PrntStr
            JSR GetErrAddr                  * Get address of error string message and send message to screen...
            JSR PrntStr
            MOVE.L #TECodeMsg,SrcPtr        * Now display Tos error code... (first place the minus sign in correct position...)
            JSR PrntStr
            MOVE.B #3,FieldWidth            * Set 2nd parameter.
            JSR Convert
            MOVE.L NumSrc,SrcPtr            * (display number)
            JSR PrntStr
            MOVE.B #12,Line
            MOVE.B #25,Column
            JSR MoveCurs
            LEA FlOpMdTabl,A0               * (Addres of disk access type error string to be shown is at FlOpMdTabl + ?FileOpMode)
            CLR.W D0
            MOVE.B FileOpMode,D0
            MOVE.L 0(A0,D0.W),SrcPtr
            JSR PrntStr
            MOVE.L #FlMsg,SrcPtr
            JSR PrntStr
            CMPI.B #Opening,FileOpMode      * If error occured during a read/write/close process...
            BGT TEContinue                  * ...then branch ( i.e. don't show Alter option as path/file name can't be changed).
            MOVE.L #TEAlterMsg,SrcPtr       * Otherwise, display Alter path/file name message...
            JSR PrntStr             
TEContinue: JSR MouseOn
NextTEScan: JSR ScanKeybrd                  * Scan keyboard.
            CMPI.B #Opening,FileOpMode      * Is the Alter Path/File Name option allowed?
            BGT SkipAltOpn                  * Branch if no.
            CMPI.B #AlterKey,ScanCode       * Otherwise, has said key been pressed?
            BEQ AnswerIsA                    * Branch if yes.
SkipAltOpn: CMPI.B #CancKey,ScanCode        * Has the Cancel key been pressed?
            BEQ AnswerIsC                   * Branch if yes.
            CMPI.B #RetryKey,ScanCode       * Has the Retry key been pressed?
            BEQ AnswerIsR                   * Branch if yes.
            MOVE.L #CancelSpec,TxtBlkSpec   * Co-ordinates for cancel box.
            JSR QHotSpot                    * Scan mouse.
            CMPI.B #No,HasClicked           * Has the user clicked on Cancel?
            BEQ RetryTest                   * Branch if no.
AnswerIsC:  MOVE.B #CancelOpn,TsErrReply    * Otherwise, set code for reply = cancel.
            BRA TosErrEnd
RetryTest:  MOVE.L #RetrySpec,TxtBlkSpec    * Co-ordinates for Retry box.
            JSR QHotSpot                    * Scan mouse.
            CMPI.B #No,HasClicked           * Has the user clicked on Retry?
            BEQ AlterTest                   * Branch if no.
AnswerIsR:  MOVE.B #RetryOption,TsErrReply  * Otherwise, set code for reply = retry.
            BRA TosErrEnd
AlterTest:  CMPI.B #Opening,FileOpMode      * Remember, no Alter option for a read/write/close error,
            BGT NextTEScan                  * so branch if this is the case.
            MOVE.L #AlterSpec,TxtBlkSpec    * Co-ordinates for Alter path/file name box.
            JSR QHotSpot                    * Scan mouse.
            CMPI.B #No,HasClicked           * Has the user clicked on Alter?
            BEQ NextTEScan                  * Branch if no.
AnswerIsA:  MOVE.B #AlterOption,TsErrReply  * Otherwise, set code for reply = alter.
TosErrEnd:  DC.W HideMouse
            JSR FillTosBox
            RTS
            

* SUBROUTINE GetYorNAns. This routine waits for the user to click on one of the two decision boxes : 'Yes' or 'No '.
*                        When done, Reply contains Yes or No.
*                        YNCoords (long word) must contain the text co-ordinates of the two boxes as follows:-

*                        Byte 0 - Yes box x-co. (left hand side),    Byte 1 - Yes box y-co.,
*                        Byte 2 - No  box x-co. (left hand side),    Byte 3 - No  box y-co..

GetYorNAns: MOVEM.L A4-A6,-(sp)             * SRTBU
            LEA YNCoords,A4                 * Set up co-ordinates specifications for QHotSpot calls in YesBlkSpec & NoBlkSpec...
            LEA YesBlkSpec,A5
            LEA NoBlkSpec,A6
            MOVE.B 1(A4),(A5)               * X-co of Yes block left,
            MOVE.B (A4),1(A5)               * Y-co of Yes block top,
            MOVE.B (A5),2(A5)               * X-co of Yes block right = X-co of Yes block left + 2,
            ADDQ.B #2,2(A5)                 * (set)
            MOVE.B (A4),3(A5)               * Y-co of Yes block bottom (same as top),
            MOVE.B 3(A4),(A6)               * X-co of No  block left,
            MOVE.B 2(A4),1(A6)              * Y-co of No  block top,
            MOVE.B (A6),2(A6)               * X-co of No  block right = X-co of No  block left + 2,
            ADDQ.B #2,2(A6)                 * (set)
            MOVE.B 2(A4),3(A6)              * Y-co of No  block bottom (same as top),
            MOVE.B (A4),Line                * Now display Yes box on screen at specified co-ordinates...
            MOVE.B 1(A4),Column
            JSR MoveCurs
            MOVE.L #YesBox,SrcPtr
            JSR PrntStr
            MOVE.B 2(A4),Line               * Now do the same with the No box...
            MOVE.B 3(A4),Column
            JSR MoveCurs
            MOVE.L #NoBox,SrcPtr
            JSR PrntStr
            JSR MouseOn
NextYNScan: JSR ScanKeybrd                  * Scan keyboard.
            CMPI.B #YesKey,ScanCode         * Has the user pressed the Yes key?
            BEQ AnswerIsY                   * Branch if yes.
            CMPI.B #NoKey,ScanCode          * Has the user pressed the No key?
            BEQ AnswerIsN                   * Branch if yes.
            MOVE.L YesBlkSpec,TxtBlkSpec    * Co-ordinates for Yes box.
            JSR QHotSpot                    * Scan mouse.
            CMPI.B #No,HasClicked           * Has the user clicked on Yes?
            BEQ TestForNo                   * Branch if no.
AnswerIsY:  MOVE.B #Yes,YNAnswer            * Otherwise, set reply = Yes.
            BRA GetYorNEnd
TestForNo:  MOVE.L NoBlkSpec,TxtBlkSpec     * Co-ordinates for No box.
            JSR QHotSpot                    * Scan mouse.
            CMPI.B #No,HasClicked           * Has the user clicked on No?
            BEQ NextYNScan                  * Branch if no.
AnswerIsN:  MOVE.B #No,YNAnswer             * Otherwise, set reply = No.
GetYorNEnd: DC.W HideMouse
            MOVE.B (A4),Line                * Now delete Yes box...
            MOVE.B 1(A4),Column
            JSR MoveCurs
            MOVE.L #BlankYNBox,SrcPtr
            JSR PrntStr
            MOVE.B 2(A4),Line               * Now do the same with the No box...
            MOVE.B 3(A4),Column
            JSR MoveCurs
            JSR PrntStr
            JSR FillWindow
            MOVEM.L (sp)+,A4-A6             * RRU
            RTS


* SUBROUTINE SndTxtLine. This routine sends one line of text to the CURRENTLY OPENED DISK FILE. The text string must be
*                        terminated with a zero byte.
*                        SrcPtr must point to the start of the line of text.
*                        FileHandle must contain the file handle number of the currently opened disk file.

SndTxtLine: JSR FindBufLen                  * Find number of characters to send.
            MOVE.L SrcPtr,-(sp)             * Address of text line.
            CLR.L -(sp)                     * Insert number of bytes (characters) to send (must be less than 256!)...
            MOVE.B BuffLength,3(sp)
            MOVE.W FileHandle,-(sp)         * File handle number of CURRENTLY OPENED FILE.
            MOVE.W #FWrite,-(sp)            * Function number for GEMDOS FWrite.
            TRAP #GemDos
            ADDA.W #12,sp                   * CS
            TST.L D0                        * Error occured?
            BMI SndError                    * Branch if yes.
            TST.W D4                        * But has the last line of text just been sent?
            BEQ SndLineEnd                  * Branch if yes.
            PEA CrgRtnLnFd                  * Otherwise, send a carriage return code ($0D) and a linefeed code ($0A)...
            MOVE.L #2,-(sp)                 * (2 bytes to send)
            MOVE.W FileHandle,-(sp)         * (Other parameters the same...)
            MOVE.W #FWrite,-(sp)
            TRAP #GemDos
            ADDA.W #12,sp                   * CS
            TST.L D0                        * Any errors?
            BPL SndLineEnd                  * Branch if no.
SndError:   MOVE.B D0,ErrorCode             * Set ErrorCode and exit.
SndLineEnd: RTS


* SUBROUTINE SendTxtFile. This routine sends the entire contents of the text file to the CURRENTLY OPENED DISK FILE.
*                         The routine does NOT close the file once finished. The code calling this routine must do this if wanted.
*                         FileStart must point to the start of the file (first character of first line).
*                         NumLines must contain the number of lines of text to send.
*                         FileHandle must contain the file handle number of the currently opened disk file.

SndTxtFile: MOVEM.L D4-D5/A4,-(sp)          * SRTBU
            CLR.L D4                        * Set counter up for DBRA loop (= NumLines - 1)...
            MOVE.W NumLines,D4
            SUBQ.L #1,D4
            MOVE.L #81,D5                   * Set increment factor for start of line pointer.
            MOVE.L FileStart,SrcPtr         * Set pointer to start of first line.
SndTxtLoop: JSR SndTxtLine                  * Send the next line of text.
            TST.B ErrorCode                 * Any errors?
            BMI SndTxtEnd                   * Branch if yes.
            ADD.L D5,SrcPtr                 * Increase pointer to next line, decrement counter & branch if not finished...
            DBRA D4,SndTxtLoop
SndTxtEnd:  MOVEM.L (sp)+,D4-D5/A4          * RRU
            RTS


* SUBROUTINE GetDskFile. This routine reads in a disk file. Method: -
*                        Standard ASCII set accepted.
*                        No control codes allowed (for now, TAB codes are ignored, but this will be changed when a TAB
*                        function is included in the program), except for the obvious (carriage return, line feed and
*                        Ctrl Z (for end-of-file marker). The file is loaded in blocks. Each block is nK long (e.g. 5K).
*                        The value of n must, after being multiplied by 1024 to give the no. of bytes, be declared as a
*                        constant via WrkSpcSize: DC.L <whatever>. Loading algorithm: An nK block of data is loaded into
*                        the workspace area from disk. This is then 'decoded' to memory. This operation is repeated until
*                        the amount of data left to load is smaller than nK. This final block is then loaded and 'decoded'
*                        to memory. Lines that exceed 80 characters: If the overflow flag ('OFW:...' on screen) is on,
*                        then such lines are wrapped - otherwise, they are cut. N.B. Of course, appropriate storage must be
*                        set aside for WorkSpace (number of long words, not bytes), e.g. IF n = 10, use   WorkSpace: DS.L 2560

GetDskFile: MOVEM.L D3-D6/A4-A6,-(sp)       * SRTBU
            JSR ClrTxtFile                  * Clear old garbage.
            MOVE.L FileSize,D3              * Calculate number of nK blocks to load plus size of final block...
            MOVE.L WrkSpcSize,D0
            DIVU.W D0,D3
            MOVE.W D3,NumBlocks             * (no. of nK blocks to load now in NumBlocks)
            SWAP D3
            MOVE.W D3,FinalBlkSz            * (size of final block to load now in FinalBlkSz)
            SWAP D3                         * Get ready for loading loop:
            MOVE.W #1,NumLines              * Initialise no. of lines in file.
            MOVE.L FileStart,DstPtr         * Initialise writing pointer for decoding.
            TST.W D3                        * Is the file to load smaller than nK?
            BEQ GetLastBlk                  * Branch if yes as only final block of size FinalBlkSz needs to be loaded.
            SUBQ.W #1,D3                    * DBRA loop used, so set to one less.
            MOVE.L WrkSpcSize,BlockSize     * Size of block to load.
LoadngLoop: JSR ReadBlock                   * Load next block of data.
            TST.B ErrorCode                 * Any errors?
            BMI BlockError                  * Branch if yes.
            MOVE.W NumLines,D0              * Set pointer to start of text line to write to...
            JSR GetLineAdr
            MOVE.L D0,LineStart
            JSR DecodeBlok                  * Data decoded.
            TST.B ErrorCode                 * Any errors?
            BMI BlockError                  * Branch if yes.
            DBRA D3,LoadngLoop              * Otherwise, branch back to load next block.            
GetLastBlk: CLR.L D0                        * Set size of final block to load...
            MOVE.W FinalBlkSz,D0
            MOVE.L D0,BlockSize
            BEQ GetDFEnd                    * But is the last block to load just nothing? If so, branch.
            JSR ReadBlock                   * Load next block of data.
            TST.B ErrorCode                 * Any errors?
            BMI BlockError                  * Branch if yes.
            MOVE.W NumLines,D0              * Set pointer to start of text line to write to...
            JSR GetLineAdr
            MOVE.L D0,LineStart
            JSR DecodeBlok                  * Data decoded.
GetDFEnd:   MOVEM.L (sp)+,D3-D6/A4-A6       * RRU
            RTS
BlockError: CMPI.B #FullTFM,ErrorCode       * Is the error just an internal flag set? (i.e. TFM full during loading)
            BNE GetDFEnd                    * Branch if no, error is more serious.
            CLR.B ErrorCode                 * Otherwise, clear error code.
            BRA GetDFEnd
            

* SUBROUTINE DecodeBlok. This routine 'decodes' a block of standard format ASCII text, storing the decoded data at DstPtr onwards.
*                        The number of bytes to decode must be in BlockSize. The start of the block to decode is always WorkSpace.
*                        The address of where the next byte would have been placed is returned in DstPtr. The address of the start
*                        of the first text line to which the data is to be sent must be in LineStart.
*                        The no. of lines of text used up is returned in NumLines (note that this must be intialised elsewhere).

DecodeBlok: MOVEM.L D3-D7/A4-A5,-(sp)       * SRTBU
            MOVE.B #Decoding,FileOpMode     * Set file operation mode in case of error.
            MOVEA.L LineStart,A4            * A4 will point to the start of each line.
            MOVEA.L #WorkSpace,A3           * A3 will point to the next byte to be read in from WorkSpace.
            MOVE.L DstPtr,A5                * A5 will point to the next place in the TFM to write to.
            MOVE.B #No,D3                   * D3 is used as a flag and is set if the last line was completely filled by characters.
            CLR.L D5                        * D5 will contain the number of bytes read in so far.
            MOVE.L DstPtr,D7                * Set D7 to contain the number of characters placed onto the current line so far...
            SUB.L A4,D7
            MOVE.L #81,D4                   * D4 will be the offset to increase A4 by to get to each new line.
            MOVE.W NumLines,D6              * D6 will contain the no. of text lines used up. Numlines is set to this at the end.
ReadLoop:   CMP.L BlockSize,D5              * Reached the end of the block yet?
            BEQ GetBlkEnd                   * Branch if yes.
            MOVE.B (A3)+,D0                 * Get next byte from WorkSpace.
            ADDQ.L #1,D5                    * Increase no. of bytes read in from workspace.
            CMPI.B #32,D0                   * Is it a valid character? (ASCII 32 -> 255)
            BPL CodeOk                      * Branch if yes. (NB. Is possible for code to be >127, but that is dealt with at CodeOk:)
            CMPI.B #9,D0                    * Is it a TAB code? (Assert: 0<= ?D0.B < 32)
            BEQ ReadLoop                    * Branch if yes (TAB codes ignored for now).
            CMPI.B #$A,D0                   * Is it a line feed?
            BEQ ReadLoop                    * Branch if yes.
            CMPI.B #$D,D0                   * Is it a carriage return?
            BEQ CarrgRetrn                  * Branch if yes.
            CMPI.B #26,D0                   * Is it an end-of-file (eof) code?
            BEQ EndOfFile                   * Branch if yes here...
            CMPI.B #$FF,D0
            BEQ EndOfFile                   * ...or here.
BadCharctr: MOVE.B #IllegalChr,ErrorCode    * All valid possibilities checked. To get to here, the code has to be invalid.
            BRA DecodeEnd                   * i.e. ?D0.B <> x, x={9, $A, $D, 26}  So, set code for an illegal character and exit.
CarrgRetrn: ADDA.L D4,A4                    * Increment start-of-line pointer.
            MOVEA.L A4,A5                   * Set writing pointer to start-of-line pointer.
            CLR.B D7                        * Reset number of characters read onto current line.
            ADDQ.W #1,D6                    * Increase the number of lines stored in the TFM.
            CMP.W MaxNoLines,D6             * Reached the end of the TFM?
            BEQ EndOfTFM                    * Branch if yes.
            CMPI.B #No,D3                   * Has an overflow condition occured?
            BEQ ReadLoop                    * Branch if no.
            MOVE.B #No,D3                   * Otherwise, clear flag and jump to place character...
            BRA TFMLineOk
CodeOk:     CMPI.B #$FF,D0                  * Byte is supposedly valid. But is it an eof code?
            BEQ EndOfFile                   * Branch if yes.
            CMPI.B #80,D7                   * (Assert: character is valid) Is the current line full?
            BMI TFMLineOk                   * Branch if no.
            CMPI.B #Yes,Overflow            * Otherwise, the line being read is too long (>80 chars). So, is overflow allowed?
            BNE FindReturn                  * Branch if no.
            MOVE.B #Yes,D3                  * Otherwise, set flag to say line is full...
            BRA CarrgRetrn                  * ...and branch to take anew line.
FindReturn: CMP.L BlockSize,D5              * Otherwise, must find next carriage return in workspace. End of workspace yet?
            BEQ GetBlkEnd                   * Branch if yes.
            MOVE.B (A3)+,D0                 * Get next byte from WorkSpace.
            ADDQ.L #1,D5                    * Increase no. of bytes read in from workspace.
            CMPI.B #$D,D0                   * Is it a return code?
            BEQ CarrgRetrn                  * Branch if yes.
            CMPI.B #26,D0                   * Is it an end-of-file (eof) code?
            BEQ EndOfFile                   * Branch if yes here...
            CMPI.B #$FF,D0
            BEQ EndOfFile                   * ...or here.
            BRA FindReturn                  * Otherwise, branch back to scan again.
TFMLineOk:  MOVE.B D0,(A5)+                 * Place character onto current line.
            ADDQ.B #1,D7                    * Increase no. of characters on current line.
            BRA ReadLoop                    * Branch back to read next byte.
GetBlkEnd:  MOVE.W D6,NumLines              * Store number of lines read in.
            MOVE.L A5,DstPtr                * Save writing pointer.
DecodeEnd:  MOVEM.L (sp)+,D3-D7/A4-A5       * RRU
            RTS
EndOfTFM:   MOVE.B #FullTFM,ErrorCode       * Set internal flag for use by GetDskFile.
            BRA GetBlkEnd
EndOfFile:  MOVE.B #UnxpctdEof,ErrorCode    * Set error for unexpected end of file and exit...
            BRA DecodeEnd


* SUBROUTINE LoadFile. This routine loads a new file in from disk. If the current text file in memory contains something, or there
*                      is no text but the file name has been set, then the routine will display an 'Are you sure?' type message -
*                      the user then decides whether to load a new file or not (the user can override the 'are you sure' bit by
*                      holding the Shift key down when pressing Alt + L).

LoadFile:   MOVE.B CursorXPos,-(sp)         * Preserve contents of certain key variables...
            MOVE.B CursorYPos,-(sp)
            DC.W HideMouse
            JSR CursrOff
            MOVE.L #LoadTitle,SrcPtr        * Show loading message...
            JSR PrntStr
            JSR SpshlKyScn                  * Has an overide loading-check key been pressed? (i.e. either Shift key)
            BTST.B #EthrShftBit,KeybrdStat
            BNE LoadFileOk                  * Branch if yes.
            CMPI.W #1,NumLines              * Otherwise, must see whether text file is empty: Is there more than one line?
            BNE AreYouSure                  * Branch if yes.
            MOVEA.L FileStart,A0            * So there's just one line - does it contain anything? (is there anything at column 1)
            TST.B (A0)
            BEQ LoadFileOk                  * Branch if no (line empty).
AreYouSure: JSR MakeWindow                  * Display 'Are you sure?' type message...
            MOVE.L #AYSInfMsg1,SrcPtr
            JSR PrntStr
GetAYSAns:  MOVE.L #$1020102D,YNCoords      * Set parameter for GetYorNAns. (Yes at line 13, col. 32; No at line 13, col. 45)
            JSR GetYorNAns                  * Get answer.
            CMPI.B #No,YNAnswer             * Does the user still want to load a file?
            BEQ LoadFlEnd                   * Branch if no.
LoadFileOk: JSR BuildPFNam                  * Construct old path/file name in OldPFName and PthFlName.
            MOVE.B #No,IgnoreOld            * Initially, old line IS to be shown.
            MOVE.B #No,InsrtState           * Initialise EnterLine text entering mode to Replace.
ChangePFNm: MOVE.L #LoadngFile,SvOrLdFlag   * Set flag so GetPFName knows which message to use.
            JSR GetPFName                   * Get the required path/file name from the user.
            CMPI.B #Yes,Cancelled           * Did the user cancel this routine?
            BEQ LdFlStop                    * Branch if yes.
            MOVE.B #No,PathScan             * Set parameters for CheckPFNam: Full path/file name scan, so set flag for CheckPFNam.
            MOVE.L #PthFileNam,SrcPtr       * Set pointer to start of path/file name.
            JSR CheckPFNam                  * Check to see path/file name entered is a valid one.
            TST.B ErrorCode                 * Was there an error?
            BMI FoundPFErr                  * Branch if yes.
            JSR FillWindow                  * Otherwise, reset the the window area and jump to seek-file sequence...
            BRA NoErrors
FoundPFErr: JSR DisplayErr                  * Show the user what is wrong with the path/file name.
            BRA ChangePFNm                  * Now jump back to re-enter path/file name.
NoErrors:   MOVE.L #PthFileNam,SrcPtr       * Must now see if the file exists.
            JSR SeekFile
            TST.B ErrorCode                 * Has the file been found?
            BEQ FileFound                   * Branch if yes.
            JSR TosError                    * Display error and get Reply.
            CMPI.B #AlterOption,TsErrReply  * Does the user want to re-enter the file name?
            BNE TstRetry                    * Branch if no.
            MOVE.B #Yes,IgnoreOld           * Otherwise, set flag so path/file name not corrupted and branch back.
            BRA ChangePFNm
TstRetry:   CMPI.B #RetryOption,TsErrReply  * Does the user wish to retry the SeekFile operation?
            BNE LoadFlEnd                   * Branch if no.  (Jump to end of this routine, as only other reply is Cancel)
            BRA NoErrors                    * Otherwise, try again (kind of ironic label to jump to, eh?)
FileFound:  JSR GetDskTrAd                  * Get current disk transfer address for file accessing.
            MOVE.L DskTrnsAdd,A1            * Can only load the file if it is a Read or Read/Write file, so check...
            TST.B 21(A1)                    * Is the file readable?   (file attribute at byte 21 in DTA buffer = 0)
            BEQ GoLoadFile                  * Branch if yes (for Read/Write).
            CMPI.B #1,21(A1)                * Is the file Read Only?  (file attribute at byte 21 in DTA buffer = 1)
            BEQ GoLoadFile                  * Branch if yes (for Read Only).
            CMPI.B #$20,21(A1)              * Is the file a written-and-closed file? (file attr. at byte 21 in DTA buffer = $20)
            BEQ GoLoadFile                  * Branch if yes (for written-and-closed).
CantLoad:   JSR MakeWindow
            MOVE.L #CantLoadEr,SrcPtr       * Otherwise, display error message...
            JSR PrntStr
            MOVE.B #16,NewCrsrYCo           * Set cursor co-ord's ready for 'Hmm...' box...
            MOVE.B #37,NewCrsrXCo
            JSR HmmMessage
            JSR FillWindow                  * Reset window area and exit...
            BRA LoadFlEnd
GoLoadFile: MOVE.L 26(A1),FileSize          * Get size of disk file from bytes 26-29 (long word) of DTA buffer.
            MOVE.L #PthFileNam,SrcPtr       * Now open file.
GoOpenFile: JSR OpenFile
            TST.B ErrorCode                 * Any errors?
            BPL OpenedOk                    * Branch if no.
            JSR TosError                    * Display error message.
            CMPI.B #AlterOption,TsErrReply  * Alter path/file name?
            BNE TstForRtry                  * Branch if no.
            MOVE.B #Yes,IgnoreOld           * Otherwise, set flag so path/file name not corrupted and branch back.
            BRA ChangePFNm
TstFrRetry: CMPI.B #RetryOption,TsErrReply  * Retry operation?
            BNE LoadFlEnd                   * Branch if no.
            BRA GoOpenFile
OpenedOk:   JSR GetDskFile                  * Read in the disk file.
            TST.B ErrorCode                 * Any errors?
            BEQ NoGDFError                  * Branch if no.
GDFError:   JSR TosError                    * Display error
            CMPI.B #RetryOption,TsErrReply  * Retry operation?
            BEQ OpenedOk                    * Branch if yes.
            JSR ClrTxtFile                  * Wipe the text memory clean.
            CLR.W TopLine                   * Reset the cursor position...
            JSR ShowText
            MOVE.W #1,NumLines              * Set new number of lines.
NoGDFError: JSR CloseFile                   * Close the disk file.
            TST.B ErrorCode                 * Any errors closing file?
            BMI GDFError                    * Branch if yes.
            MOVE.L #PthFileNam,SrcPtr       * Otherwise, since the whole load operation worked, the path/file can be stored...
            JSR SplitPFNam
            CLR.W TopLine
            MOVE.B #Yes,RemNewXPos
            MOVE.B #Yes,ScrnChange
            JSR ShowText                    * Now the new file can be displayed on screen.
            MOVE.B #4,(sp)                  * This resets the cursor position to be at top left of text window area...
            CLR.B 2(sp)
LoadFlEnd:  MOVE.L #ScrnDsply2,SrcPtr       * Re-show help message...
            JSR PrntStr
            MOVE.B MainEdMYCo,MessageYCo    * Restore values of certain key variables...
            MOVE.B MainEdMXCo,MessageXCo
            MOVE.B (sp)+,NewCrsrYCo
            MOVE.B (sp)+,NewCrsrXCo
            MOVE.B #Yes,UseMessage
            MOVE.W CurrntLine,D0            * Is the cursor position a valid one?...
            CMP.W NumLines,D0
            BLE OkLineNum                   * Branch if yes, so far.
            MOVE.W #1,D0                    * Otherwise, set to be on line one.
OkLineNum:  MOVE.W D0,LineNumber            * Set the cursor x co-ordinate...
            JSR GetLineInf     
            JSR GetOkXCo
            JSR CrsrMvAc
            MOVE.B #Yes,LineChange          * Set flag as line has changed.
            MOVE.B EdInsrtSt,InsrtState     * Reset flag.
            RTS
LdFlStop:   JSR FillWindow
            BRA LoadFlEnd


* SUBROUTINE SaveFile. This routine saves the text file via the current path/file name (if there isn't a path/file name present,
*                      the routine simply calls SaveFileAs).

SaveFile:   TST.B FileName                  * (If new file, nothing will be in the file name buffer - so check) Is there a file name?
            BNE FlNmExists                  * Branch if yes.
            JSR SaveFileAs                  * Otherwise, call SaveFileAs and exit.
            RTS
FlNmExists: MOVE.B CursorXPos,-(sp)         * Now get ready to jump to part of SaveFileAs which would be identical to the
            MOVE.B CursorYPos,-(sp)         * rest of this routine...
            JSR BuildPFNam                  * Construct path/file name.
            MOVE.B #Yes,IgnoreOld           * Set flag in case an error causes a re-entering of the path/file name.
            MOVE.B #Yes,ReplcFlag           * If the file to be saved already exists, it is to be overwritten.
            DC.W HideMouse
            JSR CursrOff
            MOVE.L #SaveTitle,SrcPtr        * Show saving message...
            JSR PrntStr
            BRA SvFlVector                  * Now jump to correct part of SaveFileAs (the part after the file name is entered)...


* SUBROUTINE SaveFileAs. This routine saves the text file via a path/file name entered by the user.

SaveFileAs: MOVE.B CursorXPos,-(sp)         * Preserve contents of certain key variables...
            MOVE.B CursorYPos,-(sp)
            DC.W HideMouse
            JSR CursrOff
            MOVE.L #SaveTitle,SrcPtr        * Show saving message...
            JSR PrntStr
            MOVE.B #No,ReplcFlag            * Since this is 'save as...', checking for a file that already exists is go.
GetPFSpec:  JSR BuildPFNam                  * Construct old path/file name in OldPFName and PthFlName.
            MOVE.B #No,IgnoreOld            * Initially, old line IS to be shown.
            MOVE.B #No,InsrtState           * Initialise EnterLine text entering mode to Relace.
AlterPFNam: MOVE.L #SavingFile,SvOrLdFlag   * Set flag so GetPFName knows which message to use.
            JSR GetPFName                   * Get the required path/file name.
            CMPI.B #Yes,Cancelled           * Did the user cancel this routine?
            BEQ SvFlStop                    * Branch if yes.
            MOVE.B #No,PathScan             * Set parameters for CheckPFNam: Full path/file name scan, so set flag for CheckPFNam.
            MOVE.L #PthFileNam,SrcPtr       * Set pointer to start of path/file name.
            JSR CheckPFNam                  * Check to see path/file name entered is a valid one.
            TST.B ErrorCode                 * Was there an error?
            BPL ErrorFree                   * Branch if no.
            JSR DisplayErr                  * Show the user what is wrong.
            BRA AlterPFNam                  * Now jump back to re-enter path/file name.
ErrorFree:  JSR FillWindow                  * Get rid of old messages.
            MOVE.L #PthFileNam,SrcPtr       * Must now see if file already exists.
            JSR SeekFile
            TST.B ErrorCode                 * Has the file been found?
            BEQ FileExsts                   * Branch if yes.
            CMPI.B #-33,ErrorCode           * Was the error 'file not found'? (which is what one wants)
            BEQ GoMakeFile                  * Branch if yes - the text file can be saved without corrupting anything.
            JSR TosError                    * Display error and get Reply.
            CMPI.B #AlterOption,TsErrReply  * Does the user want to re-enter the file name?
            BNE RetryTst                    * Branch if no.
            MOVE.B #Yes,IgnoreOld           * Otherwise, set flag so path/file name not corrupted and branch back.
            BRA AlterPFNam
RetryTst:   CMPI.B #RetryOption,TsErrReply  * Does the user wish to retry the SeekFile operation?
            BNE SvFlAsEnd                   * Branch if no.  (Jump to end of this routine, as only other reply is Cancel)
            BRA ErrorFree                   * Otherwise, try again (kind of ironic label to jump to, eh?)
FileExsts:  CMPI.B #Yes,ReplcFlag           * Show Replace message? If this routine was called from SaveFile, there may be no
            BEQ ReplcFile                   * need to bother if the file already exists - so branch if this is the case.
            JSR MakeWindow
            MOVE.L #FlExstsMsg,SrcPtr       * (place message saying file already exists on screen)
            JSR PrntStr
            MOVE.L #$0F200F2D,YNCoords      * Set parameter for GetYorNAns. (Yes at line 15, col. 32; No at line 15, col. 45)
            JSR GetYorNAns                  * Get answer.
            CMPI.B #Yes,YNAnswer            * Does the user wish to replace the file?
            BNE SvFlAsEnd                   * Branch if no.
ReplcFile:  JSR GetDskTrAd                  * Get current disk transfer address for file accessing.
            MOVE.L DskTrnsAdd,A1            * User choice: replace file. Can only do this if the file attribute = 0, so check...
            TST.B 21(A1)                    * Is the file a normal read/write file?      (file attribute at byte 21 in DTA buffer)
            BEQ GoMakeFile                  * Branch if yes.
            CMPI.B #$20,21(A1)              * (must also check for 'written and closed correctly code', which is $20. GEMDOS sets
            BEQ GoMakeFile                  * this attribute when Sorcerer saves a file (instead of attribute zero). Annoying!)
            JSR MakeWindow                  * Otherwise, display error message...
            MOVE.L #CantReplc,SrcPtr
            JSR PrntStr
            CMPI.B #1,21(A1)                * ...with accompanying detail message: Is the file write protected? (file attr. = 1)
            BNE WierdFile                   * Branch if no.
            MOVE.L #ReadOnlyFl,SrcPtr       * Otherwise, display write protect message...
            BRA ShowDtlMsg
WierdFile:  MOVE.L #NotRWFile,SrcPtr        * Display odd file message...
ShowDtlMsg: JSR PrntStr
            MOVE.B #15,NewCrsrYCo           * Display 'Hmm...' message...
            MOVE.B #37,NewCrsrXCo
            JSR HmmMessage
            JSR FillWindow
            BRA SvFlAsEnd                   * Now exit this routine.
GoMakeFile: NOP
SvFlVector: MOVE.L #PthFileNam,SrcPtr       * Now create file (old file, if any, is cleared) - set parameter for CreateFile... 
            JSR CreateFile
            TST.B ErrorCode                 * Error occured?
            BPL NoCreateEr                  * Branch if no.
            JSR TosError                    * Otherwise, display error message and get decision.
            CMPI.B #AlterOption,TsErrReply  * Alter path/file name?
            BNE TstForRtry                  * Branch if no.
            MOVE.B #Yes,IgnoreOld           * Otherwise, set flag so path/file name not corrupted and branch back.
            BRA AlterPFNam
TstForRtry: CMPI.B #RetryOption,TsErrReply  * Retry operation?
            BNE SvFlAsEnd                   * Branch if no, to end of routine as only other option is Cancel.
            BRA GoMakeFile                  * Otherwise, try again.
NoCreateEr: JSR SndTxtFile                  * Send the text file to the disk file.            
            TST.B ErrorCode                 * But did an error occur during saving?
            BPL AllDone                     * Branch if no.
            JSR TosError                    * Otherwise, display error message and get decision.
            CMPI.B #RetryOption,TsErrReply  * Try again?
            BEQ GoMakeFile                  * Branch if yes.
AllDone:    MOVE.L #PthFileNam,SrcPtr       * Finally, set drive, path and file names to correct parts of PthFileNam...
            JSR SplitPFNam
            JSR CloseFile                   * Now, or otherwise, close file and exit.
SvFlAsEnd:  MOVE.L #ScrnDsply2,SrcPtr       * Re-show help message...
            JSR PrntStr
            MOVE.B (sp)+,NewCrsrYCo         * Restore values of certain key variables...
            MOVE.B (sp)+,NewCrsrXCo
            MOVE.B MainEdMYCo,MessageYCo
            MOVE.B MainEdMXCo,MessageXCo
            MOVE.B #Yes,UseMessage
            JSR CrsrMvAc
            MOVE.B #Yes,LineChange          * Set flag as TextBuff has changed due to use of EnterLine.
            MOVE.B EdInsrtSt,InsrtState     * Reset flag.
            RTS
SvFlStop:   JSR FillWindow
            BRA SvFlAsEnd

* The following 2 routines are merely pieces of code that both LoadFile and SaveFileAs use, so to avoid needless repetition, they
* have been cut into subroutines.

GetPFName:  JSR MakeWindow                  * Set up messages, e.t.c. in the window.
            MOVE.L SvOrLdFlag,SrcPtr        * Display message saying what kind of access is to occur (saving or loading)...
            JSR PrntStr
            MOVE.L #GetPFNMess,SrcPtr
            JSR PrntStr
            MOVE.L #PthFileNam,TextBuff     * Going to use PthFileNam buffer to get the path/file name.
            MOVE.B #Yes,UseOld              * Set flag for use of OldPFName...
            MOVE.L #OldPFName,SourcePtr     * Set pointer to old path/file name. Done here so that Replace option can work.
            MOVE.B #14,BuffStY              * Set co-ordinates for start of line entering...
            MOVE.B #Yes,UseMessage          * Set flag & parameters for text enetering mode message (InsrtState set above)...
            MOVE.B #73,MessageXCo
            MOVE.B #12,MessageYCo
            JSR EnterLine                   * Get path/file name.
            RTS

DisplayErr: MOVE.L #ErrMessage,SrcPtr       * Otherwise, display error message (wipe out old message first)...
            JSR PrntStr
            JSR GetErrAddr                  * Get address of error string message and send message to screen...
            JSR PrntStr
            MOVE.L #PthFileNam,SrcPtr       * Display path/file name (wiped out by EnterLine)
            CLR.B Column
            MOVE.B #14,Line
            JSR MoveCurs
            JSR PrntStr
            MOVE.B #38,NewCrsrXCo           * Now wait for user to acknowledge message (set parameters for MouseWait)...
            MOVE.B #16,NewCrsrYCo
            JSR MouseWait
            MOVE.B #Yes,IgnoreOld           * Set flag so that the entered line isn't auto-wiped by EnterLine.
            RTS


* SUBROUTINE GetCursPos. This routine stores the current screen cursor co-ordinates.

GetCursPos: DC.W InitA                      * Get addresses of line A variables.
            MOVE.B -27(A0),CursorXPos       * Store cursor co-ordinates...
            MOVE.B -25(A0),CursorYPos
            RTS


* SUBROUTINE GetMsPos. This routine gets the absolute position of the mouse cursor, the state of the two mouse buttons, the
*                      equivalent text cursor co-ordintes of the mouse cursor, the co-ordinates of the real text screen
*                      cursor and sets/clears two flags giving an overview of any changes in the general mouse situation.

GetMsPos:   MOVEM.L D3-D4,-(sp)             * SRTBU
            JSR GetCursPos                  * Get current text cursor co-ordinates (also retrieves line A variable addresses).
            MOVE.W -602(A0),D3              * Get mouse x-co...
            MOVE.W D3,MouseXCo
            LSR.W  #3,D3
            MOVE.B D3,MwsChrXPos            * ...save equivalent text x-co,
            MOVE.W -600(A0),D3              * Get mouse y-co...
            MOVE.W D3,MouseYCo
            LSR.W #3,D3                     * This is the equivalent text y-co for medium res. mode...
            CMPI.W #2,CurrentRes            * But are we in medium res. mode?
            BNE SkipExtra                   * Branch if yes.
            LSR.W #1,D3                     * Otherwise, need an extra division by two to give text y-co for high res. mode.
SkipExtra:  CMPI.B #25,D3                   * It's possible to obtain equivalent text y-co of 25, so change to 24 if so...
            BNE YCoOk
            MOVE.B #24,D3
YCoOk:      MOVE.B D3,MwsChrYPos            * ...save equivalent text y-co,
            MOVE.B -595(A0),D3              * Get button states. Must now take copy and mask out unwanted bit in each...
            MOVE.B D3,LeftBut
            ANDI.B #1,LeftBut               * LeftBut = 1 if left button pressed, 0 if not.
            LSR.B #1,D3
            MOVE.B D3,RightBut              * RightBut = 1 if right button pressed, 0 if not.
            CLR.B D3                        * Now use D3 to assess if any situational change has occured...
            MOVE.W OldMwsXCo,D4
            CMP.W MouseXCo,D4
            BEQ NoXChange
            BSET.L #0,D3                    * Bit 0 set if mouse at new x-co...
NoXChange:  MOVE.W OldMwsYCo,D4
            CMP.W MouseYCo,D4
            BEQ NoYChange
            ORI.B #1,D3                     * Bit 0  set if mouse has moved in any direction...
NoYChange:  MOVE.W MouseXCo,OldMwsXCo       * (now old co-ords have served their purpose, set to new co-ords)
            MOVE.W MouseYCo,OldMwsYCo
            MOVE.B LeftBut,D4
            OR.B RightBut,D4
            MOVE.B D4,ButChange             * ButChange = 1 if either button pressed, 0 if not.
            OR.B D4,D3
            MOVE.B D3,MwsChange             * MwsChange = 1 if anything has changed/happened (button pressed or mouse movement).
            MOVEM.L (sp)+,D3-D4             * RRU
            RTS


* SUBROUTINE MouseOn. This routine makes the mouse cursor visible irrispective of the number of HideMouse's executed before.

MouseOn:    DC.W InitA                      * Get line A addresses.
            MOVEA.L 4(A0),A2                * Get control addresses...
            MOVEA.L 8(A0),A1
            CLR.W 2(A2)                     * Set variables required...
            MOVE.W #1,6(A2)
            CLR.W (A1)                      * Clear HideMouse count...
            DC.W ShowMouse                  * ...mouse now shown on screen.
            RTS


* SUBROUTINE MwsOutside. This routine returns Yes in MwsInside if the mouse cursor is within a specified block of text.
*                        Parameters as follows:- TxtBlkMin  = y-co. of top line of text block,
*                                                TxtBlkMax  = y-co. of bottom line of text block,
*                                                TxtBlkLeft = x-co. of leftmost column of text block,
*                                                TxtBlkRyte = x-co. of rightmost column of text block.

MwsOutside: MOVE.B #No,MwsInside            * Assume mouse not in text block (makes checking easier).
            MOVE.B MwsChrYPos,D1
            CMP.B TxtBlkMin,D1              * Is mouse above text block?
            BLT FlagOk                      * Branch if yes.
            CMP.B TxtBlkMax,D1              * Is mouse below text block?
            BGT FlagOk                      * Branch if yes.
            MOVE.B MwsChrXPos,D1
            CMP.B TxtBlkLeft,D1             * Is mouse to the left of text block?
            BLT FlagOk                      * Branch if yes.
            CMP.B TxtBlkRyte,D1             * Is mouse to the right of text block?
            BGT FlagOk                      * Branch if yes.
            MOVE.B #Yes,MwsInside           * To get to here, mouse cursor must be inside text block, so set flag.
FlagOk:     RTS


* SUBROUTINE QHotSpot. This routine returns Yes in HasClicked if one of the mouse buttons was pressed whilst the mouse cursor was
*                      over a specified block of text. The co-ordinates of the text block must be passed in TxtBlkSpec (long
*                      word). Byte 0 - X co. of text block left,    Byte 1 - Y co. of text block top,
*                             Byte 2 - X co. of text block right,   Byte 3 - Y co. of text block bottom.
*                      (e.g. For the centre 7 lines of the screen, TxtBlkSpec would = $00094F0F)

QHotSpot:   MOVE.B #No,HasClicked          * Assume false result, then test and branch if assumption correct...
            JSR GetMsPos
            JSR SetTBSpec                  * Set parameters for MwsOutside...
            JSR MwsOutside
            CMPI.B #No,MwsInside           * Inside box?
            BEQ QHtSptEnd                  * Branch if no.
            TST.B ButChange                * O.k. then, button pressed?
            BEQ QHtSptEnd                  * Branch if no.
            MOVE.B #Yes,HasClicked         * Otherwise, set flag & exit.
QHtSptEnd:  RTS


* SUBROUTINE SetTBSpec. This routine transfers the contents of TxtBlkSpec to the appropriate TxtBlk... parameters.

SetTBSpec:  LEA TxtBlkSpec,A0              * Transfer parameters...
            MOVE.B (A0)+,TxtBlkLeft
            MOVE.B (A0)+,TxtBlkMin
            MOVE.B (A0)+,TxtBlkRyte
            MOVE.B (A0),TxtBlkMax
            RTS


* SUBROUTINE MouseWait. This routine displays the message 'O.K.' in inverse at the co-ordinates specified by NewCrsrXCo and
*                       NewCrsrYCo. Once done, the routine will only exit when the user clicks the mouse on the message or
*                       presses Return (GEM copycat!).

MouseWait:  LEA TxtBlkSpec,A0               * Set pointer to QhotSpot parameter block.
            MOVE.B #3,2(A0)                 * Set offset from start to end of message (used buy WaitForClk).
            MOVE.L #OkMessage,SrcPtr        * Set pointer to 'O.K.' message.
            CLR.L D0                        * Clear flag so that Esc can't be used.
            JSR WaitForClk                  * Wait until the user has clicked on the message or pressed Return.
            MOVE.L #BlankOkMsg,SrcPtr       * Wait is over, so blank out message and exit...
            JSR PrntStr
            RTS


* SUBROUTINE HmmMessage. This routine displays the message 'Hmm...' in inverse at the co-ordinates specified by NewCrsrXCo and
*                        NewCrsrYCo. Once done, the routine will only exit when the user clicks the mouse on the message or
*                        presses Return.

HmmMessage: LEA TxtBlkSpec,A0               * Set pointer to QhotSpot parameter block.
            MOVE.B #5,2(A0)                 * Set offset from start to end of message (used buy WaitForClk).
            MOVE.L #HmmMsg,SrcPtr           * Set pointer to 'Hmm...' message.
            CLR.L D0                        * Clear flag so that Esc can't be used.
            JSR WaitForClk                  * Wait until the user has clicked on the message or pressed Return.
            MOVE.L #BlankHmMsg,SrcPtr       * Wait is over, so blank out message and exit...
            JSR PrntStr
            RTS


* SUBROUTINE MoreMessg. This routine displays the message 'More...' in inverse at the co-ordinates specified by NewCrsrXCo and
*                       NewCrsrYCo. Once done, the routine will exit when the user clicks the mouse on the message or
*                       presses Return. The user can also press ESC as a canceling device (in which case Cancelled = Yes).

MoreMessg:  LEA TxtBlkSpec,A0               * Set pointer to QhotSpot parameter block.
            MOVE.B #6,2(A0)                 * Set offset from start to end of message (used buy WaitForClk).
            MOVE.L #MoreMsg,SrcPtr          * Set pointer to 'More...' message.
            MOVE.L #-1,D0                   * Set flag so that Esc can be used.
            JSR WaitForClk                  * Wait until the user has clicked on the message or pressed Return.
            MOVE.L #BlankMrMsg,SrcPtr       * Wait is over, so blank out message and exit...
            JSR PrntStr
            RTS

* This is a routine which the wait-for-click-on-message routines use. Done to avoid code repetition.
* If ESC is allowed to be used, D0 should contain $FFFFFFFF (or -1) when this routine is called.
* (If Esc was allowed, and was pressed, Cancelled returns Yes - otherwise, Cancelled contains No)

WaitForClk: MOVE.B #No,CancFlag             * Assume Esc can't be used.
            CMPI.L #-1,D0                   * Is ESC to be used?
            BNE NoEsc                       * Branch if no.
            MOVE.B #Yes,CancFlag            * Otherwise, set use flag and initialise result flag...
            MOVE.B #No,Cancelled
NoEsc:      MOVE.B NewCrsrXCo,D0            * Set the rest of the parameters needed by QHotSpot...
            ADD.B D0,2(A0)
            MOVE.B NewCrsrXCo,(A0)
            MOVE.B NewCrsrYCo,1(A0)
            MOVE.B NewCrsrYCo,3(A0)
            DC.W HideMouse                  * Screen is changing, so hide mouse.
            JSR CrsrMvAc                    * Assert: The NewCrsr_Co co-ordinates have already been set.
            JSR ReverseOn
            JSR PrntStr
            JSR ReverseOff
            JSR MouseOn                     * Mouse active.
WaitLoop:   JSR ScanKeybrd                  * Scan keyboard.
            CMPI.B #ReturnKey,ScanCode      * Has Return been pressed?
            BEQ WaitOver                    * Branch if yes.
            CMPI.B #Yes,CancFlag            * Is Esc allowed?
            BNE EscNotAlwd                  * Branch if no.
            CMPI.B #EscKey,ScanCode         * Otherwise, has Esc been pressed?
            BNE EscNotPrsd                  * Branch if no.
            MOVE.B #Yes,Cancelled           * Otherwise, set flag and exit.
            BRA WaitOver
EscNotPrsd:
EscNotAlwd: JSR QHotSpot                    * Scan mouse.
            CMPI.B #No,HasClicked           * Has user clicked on the message box?
            BEQ WaitLoop                    * Branch if no.
WaitOver:   DC.W HideMouse
            JSR CrsrMvAc
            RTS



* SUBROUTINE CopyData. This routine copys a section of bytes (such as a text buffer), from one place in memory to another.
*                      SrcPtr must point to the source start byte (lowest in memory),
*                      DstPtr must point to the desination start byte,
*                      LineLen must contain the number of bytes to copy.


CopyData:   MOVEA.L SrcPtr,A1               * Set pointer to source.
            MOVEA.L DstPtr,A2               * Set pointer to destination.
            CLR.L D1                        * Calculate address at which copy routine should terminate ( = DstPtr + LineLen )...
            MOVE.B LineLen,D1
            ADD.L A2,D1
MoreToCopy: CMP.L A2,D1                     * Reached the end of the data line yet?
            BEQ CopyEnd                     * Branch if yes.
            MOVE.B (A1)+,(A2)+              * Otherwise, copy next byte.
            BRA MoreToCopy
CopyEnd:    RTS


* SUBROUTINE CopiData. This routine copys a section of bytes (such as a text buffer), from one place in memory to another.
*                      SorcPtr must point to the source start byte (lowest in memory),
*                      DestPtr must point to the desination start byte,
*                      LineLen must contain the number of bytes to copy.


CopiData:   MOVEA.L SorcPtr,A1              * Set pointer to source.
            MOVEA.L DestPtr,A2              * Set pointer to destination.
            CLR.L D1                        * Will overlap occur during copying? (Does if (DestPtr - SorcPtr) - NumBytes < 0)
            MOVE.B NumBytes,D1
            MOVE.L A2,D0
            SUB.L A1,D0
            SUB.L D1,D0
            BMI Overlap                     * Branch if yes.
            ADD.L A2,D1                     * Otherwise, calculate loop termination address.
MoreToCopi: CMP.L A2,D1                     * Reached the end of the data line yet?
            BEQ CopiEnd                     * Branch if yes.
            MOVE.B (A1)+,(A2)+              * Otherwise, copy next byte.
            BRA MoreToCopi
CopiEnd:    RTS
Overlap:    CLR.L D1                        * D1 used in case of overflow.
            CMPA.L MaxAddress,A2            * Would copying cause an overflow?
            BLE NoOverflo                   * Branch if no.
            MOVE.L A2,D1                    * Otherwise, decrease both pointers by the number of bytes that would overflow...
            SUB.L MaxAddress,D1
            SUBA.L D1,A1
            MOVE.L MaxAddress,A2
NoOverflo:  CLR.L D0                        * Calculate loop termination address...
            MOVE.B LineLen,D0
            CMP.B D1,D0                     * It is possible, during word manipulation, that none of the data need be copied...
            BMI Copi2End                    * Branch if this is the case.
            MOVE.L SrcPtr,D1
            SUB.L D0,D1
MrToCopi2:  CMP.L A1,D1                     * Termination address reached yet?
            BEQ Copi2End                    * Branch if yes.
            MOVE.B -(A1),-(A2)              * Otherwise, copy next byte and loop again.
            BRA MrToCopi2
Copi2End:   RTS


CopyData2:  MOVEA.L SrcPtr,A1               * Set pointer to source.
            MOVEA.L DstPtr,A2               * Set pointer to destination.
            CLR.L D1                        * D1 used in case of overflow.
            CMPA.L MaxAddress,A2            * Would copying cause an overflow?
            BLE NoOverflow                  * Branch if no.
            MOVE.L A2,D1                    * Otherwise, decrease both pointers by the number of bytes that would overflow...
            SUB.L MaxAddress,D1
            SUBA.L D1,A1
            MOVE.L MaxAddress,A2
NoOverflow: CLR.L D0                        * Calculate loop termination address...
            MOVE.B LineLen,D0
            CMP.B D1,D0                     * It is possible, during word manipulation, that none of the data need be copied...
            BMI Copy2End                    * Branch if this is the case.
            MOVE.L SrcPtr,D1
            SUB.L D0,D1
MrToCopy2:  CMP.L A1,D1                     * Termination address reached yet?
            BEQ Copy2End                    * Branch if yes.
            MOVE.B -(A1),-(A2)              * Otherwise, copy next byte and loop again.
            BRA MrToCopy2
Copy2End:   RTS


* SUBROUTINE ClearData. This routine clears a section of bytes in memory (such as a text buffer) by filling it with zeros.
*                       SrcPtr must point to the source start byte (lowest in memory),
*                       LineLen must contain the number of bytes to clear.

ClearData:  TST.B LineLen                   * Any to clear?
            BNE GoClrData                   * Branch if yes.
            RTS
GoClrData:  MOVEA.L SrcPtr,A1               * Set pointer to start of data area to be cleared.
            CLR.W D1                        * Set counter for DBRA...
            MOVE.B LineLen,D1
            SUBQ.B #1,D1
MoreToClr:  CLR.B (A1)+                     * Clear next byte.
            DBRA D1,MoreToClr               * Branch back if not finished.
            RTS


* SUBROUTINE RemoveData. This routine removes a series of characters from a line of text and stores them in a temporary buffer.
*                        The parameters required are the same as DeleteData. When finished, the removed characters are stored at
*                        WordStore onwards, with the number of characters stored in WrdStrLen.

RemoveData: CLR.L D2                        * First copy character block into word store, so calculate parameters for CopyData...
            MOVE.B Position,D2
            ADD.L TextBuff,D2
            MOVE.L D2,SrcPtr
            MOVE.L #WordStore,DstPtr
            MOVE.B NumChars,LineLen
            JSR CopyData                    * Characters saved.
            JSR DeleteData                  * Now delete the unwanted characters.
            MOVE.B NumChars,WrdStrLen       * Finally set the length of the word dumped in WordStore.
            RTS


* SUBROUTINE InsertData. This routine does the precise opposite of RemoveData. The parameters required are the same as DeleteData,
*                        though NumChars isn't needed (WrdStrLen is used).If there is something in WordStore (in which case
*                        WrdStrLen <> 0) after the characters are inserted at Position in the text line WrdStrLen is set to zero
*                        (thus preventing something accidentally being inserted twice). With this facility, words can be moved
*                        around very easily with the mouse.

InsertData: CLR.L D2                        * Move characters rightwards in text line to provide space for word to be inserted...
            MOVE.B BuffLength,D2            * SrcPtr = TextBuff + BuffLength...
            ADD.L TextBuff,D2
            MOVE.L D2,SrcPtr
            CLR.L D2                        * DstPtr = SrcPtr + WrdStrLen...
            MOVE.B WrdStrLen,D2
            ADD.L SrcPtr,D2
            MOVE.L D2,DstPtr
            MOVE.B BuffLength,D2            * LineLen = BuffLength - Position.
            SUB.B Position,D2
            MOVE.B D2,LineLen
            JSR CopyData2                   * Space for data to be inserted now ready.
            CLR.L D2                        * Must now copy word/data to be inserted.
            MOVE.B Position,D2              * DstPtr = TextBuff + Position + WrdStrLen...
            ADD.B WrdStrLen,D2
            ADD.L TextBuff,D2
            MOVE.L D2,DstPtr
            CLR.L D0                        * SrcPtr = WordStore + WrdStrLen.
            MOVE.B WrdStrLen,D0
            ADD.L #WordStore,D0
            MOVE.L D0,SrcPtr
            MOVE.B WrdStrLen,LineLen        * LineLen = WrdStrLen
            JSR CopyData2                   * Data inserted.
            MOVE.B BuffLength,D2            * Increase buffer length to new value...
            ADD.B WrdStrLen,D2
            CMP.B MaxLineLen,D2             * Was there an overflow?
            BLE BufflenOk                   * Branch if no.
            MOVE.B MaxLineLen,D2            * Otherwise, set to maximum length.
BufflenOk:  MOVE.B D2,BuffLength
            CLR.B WrdStrLen                 * Finally, must 'delete' the contents of WordStore by setting WrdStrLen to zero.
            RTS


* SUBROUTINE DeleteData. This routine totally deletes a series of characters from a text line. This would be similar to executing
*                        the routine DeleteAc several times at one position.
*                        TextBuff must point to the start of the text line,
*                        BuffLength must contain the number of characters in the line,
*                        Position must be an offset from TextBuff to the first unwanted character.
*                        NumChars must contain the number of characters to delete (starting from Position and going rightwards
*                        - or up in memory).

DeleteData: MOVEM.L D4-D5/A4,-(sp)          * SRTBU
            MOVEA.L TextBuff,A4             * TextBuff used in later calculations.
            MOVE.B Position,D4              * This routine uses CopyData and ClearData - so find SrcPtr & DstPtr for CopyData...
            ADD.B NumChars,D4               * Position + NumChars is used a couple of times so this will save repetition.
            CLR.L D5                        * Find DstPtr for CopyData...       DstPtr  = TextBuff + Position
            MOVE.B Position,D5
            ADD.L A4,D5
            MOVE.L D5,DstPtr
            CLR.L D5                        * Find SrcPtr for CopyData...       SrcPtr  = TextBuff + Position + NumChars
            MOVE.B D4,D5
            ADD.L A4,D5
            MOVE.L D5,SrcPtr
            MOVE.B BuffLength,D5            * Find LineLen for CopyData...      LineLen = BuffLength - ( Position + NumChars )
            SUB.B D4,D5
            MOVE.B D5,LineLen
            JSR CopyData                    * This overwrites the unwanted characters with the rest of the line.
            CLR.L D5                        * Must now delete garbage left after new end of line, so find SrcPtr for ClearData...
            MOVE.B BuffLength,D5
            SUB.B NumChars,D5
            ADD.L A4,D5                     * SrcPtr = TextBuff + ( BuffLength - NumChars )
            MOVE.L D5,SrcPtr
            MOVE.B NumChars,LineLen         * LineLen = NumChars.
            JSR ClearData                   * Unwanted garbage deleted.
            MOVE.B BuffLength,D5            * Must now set BuffLength to the  new length of the line:-
            SUB.B NumChars,D5               * BuffLength = BuffLength - NumChars.
            MOVE.B D5,BuffLength
            MOVEM.L (sp)+,D4-D5/A4          * RRU
            RTS


* These 2 routines act on distinct lines of text within the TFM area. No change is made to the screen:-

* SUBROUTINE CopyTLine. This routine copies the contents of one text line to another.
*                       SrcPtr must contain the start address of the line to be copied,
*                       DstPtr must contain the start address of the line to be copied to.

CopyTLine:  MOVEA.L SrcPtr,A1               * Get pointers ready for loop...
            MOVEA.L DstPtr,A2
            MOVEQ #79,D1                    * Each line is 80 bytes long (Must set counter to one less for DBRA).
CopyLnLoop: MOVE.B (A1)+,(A2)+              * Copy the line and exit...
            DBRA D1,CopyLnLoop
            RTS


* SUBROUTINE ClearTLine. This routine clears one text line within the TFM area by filling it with zeros. SrcPtr must point to the
*                        start of the line in memory.

ClearTLine: MOVEA.L SrcPtr,A1               * Set pointer and counter (counter for 80 bytes, one less for DBRA)...
            MOVEQ #79,D1
ClrTLnLoop: CLR.B (A1)+                     * Clear the line and exit...
            DBRA D1,ClrTLnLoop
            RTS


* SUBROUTINE CutLine. This routine cuts part of one line (from a given position to the end of the line) onto a newly created next
*                     line. Pressing Return in the middle of the file would require this routine.
*                     LineNumber must contain the line at which to cut.
*                     Position must contain the position in the line from which to cut.

CutLine:    MOVE.B BuffLength,-(sp)         * Save variable which will be corrupted.
            MOVE.W LineNumber,D0            * Does a blank line need to be created?
            CMP.W NumLines,D0
            BEQ CutLnCont                   * Branch if no.
            MOVE.W LineNumber,ShntLnStrt    * First create the blank line needed...
            ADDQ.W #1,ShntLnStrt
            MOVE.W #1,ShuntDist
            JSR ShuntDown
            BRA NoLnIncrs                   * Skip increase of number of lines, as ShuntDown does this.
CutLnCont:  ADDQ.W #1,NumLines              * Increase number of lines in file.
NoLnIncrs:  MOVE.W LineNumber,D0            * Calculate number of characters to copy to blank line...
            JSR GetLineAdr                  * (must first get address of the line for FindBufLen)
            MOVE.L D0,-(sp)                 * (store for further use)
            MOVE.L D0,SrcPtr
            JSR FindBufLen
            CLR.L D0
            MOVE.B BuffLength,D0
            SUB.B Position,D0
            MOVE.B D0,LineLen
            CLR.L D0                        * Calculate start address of character block to be copied...
            MOVE.B Position,D0
            ADD.L (sp),D0
            MOVE.L D0,SrcPtr
            MOVE.L (sp)+,DstPtr             * Find start address of blank line...
            ADDI.L #81,DstPtr
            JSR CopyData                    * Copy the character block to be cut to the new blank line.
            JSR ClearData                   * Now delete the unwanted old character block.
            MOVE.B (sp)+,BuffLength         * Restore corrupted variable.
            RTS


* SUBROUTINE JoinLine. This routine does the exact opposite of CutLine.
*                      LineNumber must contain the line number of the line to be joined backwards to the line before.
*                      The length of the newly created line is returned in BuffLength.
*                      The position on the newly created line where the old line gets attached is returned in Position.

JoinLine:   MOVEM.L D5-D7,-(sp)             * SRTBU
            MOVE.B #Yes,ScrnChange          * Assume the join action will be allowed.
            MOVE.W LineNumber,D0            * Get start address of line LineNumber...
            JSR GetLineAdr
            MOVE.L D0,D7
            MOVE.L D7,SrcPtr                * Get length of line LineNumber...
            JSR FindBufLen
            MOVE.B BuffLength,D5
            MOVE.B D5,D6                    * Take a copy for later use.
            SUBI.L #81,SrcPtr               * Get start address of line LineNumber - 1...
            JSR FindBufLen                  * Get length of this line.
            ADD.B BuffLength,D5             * Calculate length of the line that would be formed.
            ANDI.W #$FF,D5                  * (mask out unwanted byte ready for CMPI...)
            CMPI.W #80,D5                   * Is it too long?
            BLE JoinLineOk                  * Branch if no.
            MOVE.B #No,ScrnChange           * Otherwise, switch flag and exit...
            BRA JoinLnEnd
JoinLineOk: MOVE.L SrcPtr,DstPtr            * Linkup is go - find start address of where character block is to be copied to...
            CLR.L D0
            MOVE.B BuffLength,D0
            ADD.L D0,DstPtr
            ADDI.L #81,SrcPtr               * Get start address of character block (i.e. address of line LineNumber).
            MOVE.B D6,LineLen               * Set number of characters to copy.
            JSR CopyData
            MOVE.W NumLines,D0              * Is line LineNumber the last line in the text memory?
            CMP.W LineNumber,D0
            BNE MustShunt                   * Branch if no - the rest of the text below the joined line must be shunted upwards.
            JSR ClearTLine                  * Otherwise, just clear he last line.
            SUBI.W #1,NumLines              * Decrease number of lines in text memory and jump to result finding code...
            BRA FindBandP
MustShunt:  MOVE.W LineNumber,ShntLnStrt    * Shunt text up one position at line LineNumber + 1...
            ADDI.W #1,ShntLnStrt
            MOVE.W #1,ShuntDist
            JSR ShuntUp
FindBandP:  MOVE.B BuffLength,Position      * Store position where join occured.
            MOVE.B D5,BuffLength            * Store length of newly created line.
JoinLnEnd:  MOVEM.L (sp)+,D5-D7             * RRU
            RTS 


* SUBROUTINE GetLineAdr. This routine returns the start address of a text line.
*                        D0 must contain the physical line number (i.e. ?D0.W := {1..NumLines});
*                        the resulting address is returned in D0.

GetLineAdr: ANDI.L #$FFFF,D0                * Mask out unwanted upper word of D0.
            SUBQ.W #1,D0                    * Decrement to give line offset from start of text file.
            MULU.W #81,D0                   * Convert to an actual address and exit...
            ADD.L FileStart,D0
            MOVE.L D0,LineStart
            RTS


* SUBROUTINE ShuntDown. This routine effectively inserts a given number of blank lines into the TFM.
*                       There is no change on screen. This must be handled, if necessary by outside sources.
*                       ShuntDist must contain the physical number of blank lines to be inserted,
*                       ShntLnStrt must contain the physical line number (i.e. 1..NumLines) where the blank lines are to be
*                       inserted.

ShuntDown:  MOVEM.L D4-D5,-(sp)             * SRTBU
            MOVE.W NumLines,D0              * Lines will be copied last first: Find address of where last line will be copied to:
            ADD.W ShuntDist,D0
            JSR GetLineAdr
            MOVE.L D0,DstPtr                * (initial desination pointer set)
            MOVE.W NumLines,D0              * Find address of last line of text...
            JSR GetLineAdr
            MOVE.L D0,SrcPtr                * (initial source pointer set)
            MOVE.W NumLines,D4              * Set loop counter for number of lines to copy (physical number less one for DBRA )...
            SUB.W ShntLnStrt,D4
            MOVEQ #81,D5                    * Set line decrement factor (each line is 81 bytes away from the next).
CopyDnLnLp: JSR CopyTLine                   * Copy next line...
            SUB.L D5,SrcPtr                 * Decrement pointers for next line and branch back if not finished...
            SUB.L D5,DstPtr
            DBRA D4,CopyDnLnLp
            MOVE.L DstPtr,SrcPtr            * Clearing the unwanted old lines at ShntLnStrt gives the inserted blank lines...
            MOVE.W ShuntDist,D4             * Set loop counter to no. lines to clear less one for DBRA...
            SUBQ.W #1,D4
ClrDnLnBlk: JSR ClearTLine                  * Clear next line..
            SUB.L D5,SrcPtr                 * ...move pointer to next line & branch back if not finished...
            DBRA D4,ClrDnLnBlk
            MOVE.W ShuntDist,D0             * Now increase length of text file by the number of lines inserted...
            ADD.W D0,NumLines
            MOVEM.L (sp)+,D4-D5             * RRU
            RTS


* SUBROUTINE ShuntUp. This routine effectively removes a given number of lines from the TFM (no change on screen).
*                     ShuntDist must contain the physical number of lines to be removed (totally deleted),
*                     ShntLnStrt must contain the physical line number (i.e. 1..NumLines) of the first line of the block of
*                     lines that will be moved upwards to delete the unwanted lines (e.g. to delete line k, ShntLnStrt = k + 1).

ShuntUp:    MOVEM.L D4-D5,-(sp)             * SRTBU
            MOVE.W ShntLnStrt,D0            * Get address of first line of line block to be moved up...
            JSR GetLineAdr
            MOVE.L D0,SrcPtr
            MOVE.W ShntLnStrt,D0            * Get address of where first line is to be copied to...
            SUB.W ShuntDist,D0
            JSR GetLineAdr
            MOVE.L D0,DstPtr
            MOVE.W NumLines,D4              * Calculate loop counter for number of lines to copy (less one for DBRA)...
            SUB.W ShntLnStrt,D4
            MOVEQ.L #81,D5                  * Set line increment factor (each line is 81 bytes away from the next).
CopyUpLnLp: JSR CopyTLine                   * Copy next line...
            ADD.L D5,SrcPtr                 * Increment pointers for next line and branch back if not finished...
            ADD.L D5,DstPtr
            DBRA D4,CopyUpLnLp
            MOVE.L DstPtr,SrcPtr            * Must now clear the unwanted old lines at the end of the file...
            MOVE.W ShuntDist,D4             * Set loop counter to no. lines to clear less one for DBRA...
            SUBQ.W #1,D4
ClrUpLnBlk: JSR ClearTLine                  * Clear next line..
            ADD.L D5,SrcPtr                 * ...move pointer to next line & branch back if not finished...
            DBRA D4,ClrUpLnBlk
            MOVE.W ShuntDist,D0             * Now decrease length of text file by the number of lines removed...
            SUB.W D0,NumLines
            MOVEM.L (sp)+,D4-D5             * RRU
            RTS


* SUBROUTINE FindBufLen. This routine finds the length of a text line by finding the first zero (up to a certain maximum). Result
*                        returned in BuffLength.
*                        SrcPtr must point to the first byte of the text line, MaxLineLen must be the maximum length.

FindBufLen: MOVEA.L SrcPtr,A1               * Pointer to first byte.
            CLR.L D2                        * Calculate highest allowable address of a character (in A2)...
            MOVE.B MaxLineLen,D2
            ADD.L A1,D2
FindLength: CMPA.L D2,A1                    * Reached maximum length of line?
            BEQ FindLen                     * Branch if yes.
            TST.B (A1)+                     * Found a zero?
            BNE FindLength                  * Branch if no.
            SUBQ.L #1,A1                    * Must adjust A1 if maximum line length reached, otherwise result would be wrong.
FindLen:    MOVE.L A1,D1
            SUB.L SrcPtr,D1
            MOVE.B D1,BuffLength            * Save length of text line.
            RTS


* FUNCTION GetLineLen. This routine gets the length of a particular text line whose number is in LineNumber. The result is returned
*                      in LineLength.

GetLineLen: MOVEA.L LineStart,A0            * Set pointer to start of line.
            TST.B (A0)                      * Is the line empty?
            BNE GLLCont1                    * Branch if no.
            CLR.B LineLength                * Otherwise, set length to zero.
            RTS
GLLCont1:   MOVE.L #$FF,D2                  * Algorithm is binary search (find first zero). Set Upper to MaxLineLen - 1...
            ADD.B MaxLineLen,D2
            TST.B 0(A0,D2.W)                * Is the line totally full?
            BEQ GLLCont2                    * Branch if no.
            MOVE.B MaxLineLen,LineLength    * Otherwise, set length to maximum.
            RTS
GLLCont2:   MOVEQ #0,D0                     * Set Lower to zero.
            MOVEQ #0,D1                     * Clear garbage in Middle.
GetLenLoop: MOVE.B D0,D1                    * Set Middle = (Lower + Upper) DIV 2...
            ADD.B D2,D1
            LSR.B #1,D1
            TST.B 0(A0,D1.W)                * Is there a character there?
            BNE CharFound                   * Branch if yes.
            MOVE.B D1,D2                    * Otherwise, set Upper = Middle.
            CMP.B D0,D2                     * Finished yet?
            BNE GetLenLoop                  * Branch if no.
            MOVE.B D0,LineLength            * Otherwise, set length of line.
            RTS
CharFound:  MOVE.B D1,D0                    * Set Lower = Middle + 1...
            ADDQ.B #1,D0
            CMP.B D0,D2                     * Finished yet?
            BNE GetLenLoop                  * Branch if no.
            MOVE.B D0,LineLength            * Otherwise, set length of line.
            RTS


* SUBROUTINE NxtSpcLft. This routine finds the next space in a line of text. 
*                       TextBuff must point to the start of the text line. BuffLength must contain the length of the line.
*                       Position must contain the position in the line where the search routine is to be begin looking (offset
*                       from the start of the line, therefore Position = 0,1,2,... et.c.). These parameters are required for all
*                       four character search routines.

NxtSpcLeft: MOVEA.L TextBuff,A1             * Set pointer to start of text buffer.
            CLR.W D1                        * Transfer position for indirect index register use.
            MOVE.B Position,D1
            CMP.B BuffLength,D1             * If position is after last character, set to last character.
            BNE LftSpcScan                  * Branch if not.
            TST.B BuffLength                * Can't do this if at start of line though.
            BEQ LeftSpcEnd                  * Branch if so.
            SUBQ.B #1,D1
LftSpcScan: CMPI.B #' ',0(A1,D1.W)          * Is there a space there?
            BEQ LeftSpcEnd                  * Branch if yes.
            CMPI.B #'\',0(A1,D1.W)          * Is there a backslash? (this is to allow removal of file name in LoadFile, et.c.)
            BEQ LeftSpcEnd                  * Branch if yes (this check-for-a-backslash is done in all the Nxt... routines).
            TST.B D1                        * Has the start of the line been reached?
            BEQ LeftSpcEnd                  * Branch if yes.
            SUBQ.B #1,D1                    * Otherwise, move position left one.
            BRA LftSpcScan
LeftSpcEnd: MOVE.B D1,Position              * Pass back result of search.
            RTS


* SUBROUTINE NxtSpcRyte. This routine finds the next rightwards in a line of text. Same parameters as NxtSpcLeft.

NxtSpcRyte: MOVEA.L TextBuff,A1             * Set pointer to start of text buffer.
            CLR.W D1                        * Transfer position for indirect index register use.
            MOVE.B Position,D1
RytSpcScan: CMPI.B #' ',0(A1,D1.W)          * Is there a space there?
            BEQ RyteSpcEnd                  * Branch if yes.
            CMPI.B #'\',0(A1,D1.W)          * Is there a backslash?
            BEQ RyteSpcEnd                  * Branch if yes.
            CMP.B BuffLength,D1             * Has the end of the line been reached yet?
            BEQ RyteSpcEnd                  * Branch if yes.
            ADDQ.B #1,D1                    * Otherwise, move position right one.
            BRA RytSpcScan
RyteSpcEnd: MOVE.B D1,Position              * Pass back result of search.
            RTS


* SUBROUTINE NxtChrLeft. This routine finds the next non-space character leftwards. Same parameters as NxtSpcLeft.

NxtChrLeft: MOVEA.L TextBuff,A1             * Set pointer to start of text buffer.
            CLR.W D1                        * Transfer position for indirect index register use.
            MOVE.B Position,D1
            CMP.B BuffLength,D1             * If position is after last character, set to last character.
            BNE LftChrScan                  * Branch if not.
            TST.B BuffLength                * Can't do this if at start of line though.
            BEQ LeftChrEnd                  * Branch if so.
            SUBQ.B #1,D1
LftChrScan: CMPI.B #' ',0(A1,D1.W)          * Is there a character there?
            BEQ NxtLCSTest                  * Branch if no here...
            CMPI.B #'\',0(A1,D1.W)
            BEQ NxtLCSTest                  * ...or here.
            BRA LeftChrEnd                  * Otherwise, jump to end.
NxtLCSTest: TST.B D1                        * Has the start of the line been reached?
            BEQ LeftChrEnd                  * Branch if yes.
            SUBQ.B #1,D1                    * Otherwise, move position left one.
            BRA LftChrScan
LeftChrEnd: MOVE.B D1,Position              * Pass back result of search.
            RTS


* SUBROUTINE NxtChrRyte. This routine finds the next non-space character rightwards. Same parameters as NxtSpcLeft.

NxtChrRyte: MOVEA.L TextBuff,A1             * Set pointer to start of text buffer.
            CLR.W D1                        * Transfer position for indirect index register use.
            MOVE.B Position,D1
RytChrScan: CMPI.B #' ',0(A1,D1.W)          * Is there a character there?
            BEQ NxtRCSTest                  * Branch if no here...
            CMPI.B #'\',0(A1,D1.W)
            BEQ NxtRCSTest                  * ...or here.
            BRA RyteChrEnd                  * Otherwise, jump to end.
NxtRCSTest: CMP.B BuffLength,D1             * Has the end of the line been reached?
            BEQ RyteChrEnd                  * Branch if yes.
            ADDQ.B #1,D1                    * Otherwise, move position right one.
            BRA RytChrScan
RyteChrEnd: MOVE.B D1,Position              * Pass back result of search.
            RTS


* SUBROUTINE RestoreOld. This routine restores and old line of text to a buffer.
*                        Maximum allowed length of line must be in MaxLineLen,
*                        SrcPtr must point to start of text line,
*                        DstPtr must point to where the line is to be restored to,
*                        Length of text line returned in BuffLength.

RestoreOld: MOVE.B MaxLineLen,LineLen       * Clear destination area (memory)...
            MOVE.L TextBuff,SrcPtr
            JSR ClearData
            CLR.B Column                    * Clear destination area (screen)...
            MOVE.B BuffStY,Line
            JSR MoveCurs
            JSR WipeLine
            MOVE.L SourcePtr,SrcPtr         * Find length of text line and copy to destination...
            MOVE.L TextBuff,DstPtr
            JSR FindBufLen
            MOVE.B BuffLength,LineLen
            JSR CopyData
            MOVE.L DstPtr,SrcPtr            * Send copied line to screen...
            JSR PrntStr
            MOVE.B BuffLength,NewCrsrXCo    * Move cursor to end of line.
            JSR CrsrMvAc
            RTS


* SUBROUTINE UpdateMesg. This routine is called when the entering mode changes. Nothing happens if (UseMessage) = No.

UpdateMesg: CMPI.B #No,UseMessage           * Show a message concerning entering mode?
            BEQ UpdateEnd                   * Branch if no.
            CMPI.B #Yes,InsrtState          * Is insert mode on?
            BEQ UseInsMess                  * Branch if yes.
            MOVE.L #ReplcMess,SrcPtr        * Set pointer ready for PrntStr.
            BRA SendMess
UseInsMess: MOVE.L #InsertMess,SrcPtr       * Set pointer ready for PrntStr.
SendMess:   DC.W InitA                      * Save current cursor position (musn't destroy CursorX/YPos, so do it directly)...
            MOVE.B -27(A0),-(sp)
            MOVE.B -25(A0),-(sp)
            MOVE.B MessageXCo,Column        * Move cursor to message start and send message...
            MOVE.B MessageYCo,Line
            JSR MoveCurs
            JSR PrntStr
            MOVE.B (sp)+,Line               * Move cursor back to original position.
            MOVE.B (sp)+,Column
            JSR MoveCurs
UpdateEnd:  RTS


* SUBROUTINE ClearMess. When whatever routine no longer needs the entering mode message, this routine is called.
*                       Nothing happens if (UseMessage) = No.

ClearMess:  CMP.B #No,UseMessage            * Was a message shown?
            BEQ NoMessage                   * Branch if no.
            MOVE.B MessageXCo,Column
            MOVE.B MessageYCo,Line
            JSR MoveCurs
            MOVE.L #BlankMess,SrcPtr        * Address of blank message in SrcPtr ready for PrntStr.
            JSR PrntStr
NoMessage:  RTS


* SUBROUTINE ClrEntrLn. This routine is called by EnterLine when it finishes. The code blanks out the text line with spaces.

ClrEntrLn:  CLR.B Column                    * Move cursor to start of text line.
            MOVE.B BuffStY,Line
            JSR MoveCurs
            MOVE.L #80,D1                   * Calculate length of blank string to be displayed...
            SUB.B MaxLineLen,D1
            MOVE.L #BlankStrng,SrcPtr
            ADD.L D1,SrcPtr
            JSR PrntStr                     * Blank out line.
            RTS


* SUBROUTINE UpdateScrn. This routine is called by EnterLine after a particular action has been executed. It updates the current
*                        state of the text line on the screen. 

UpdateScrn: DC.W InitA                      * Save current cursor position (musn't destroy CursorX/YPos, so do it directly)...
            MOVE.B -27(A0),-(sp)
            MOVE.B -25(A0),-(sp)
            MOVE.B BuffLength,D0            * If the text line is totally full, no need to clear after end of line, so check...
            CMP.B MaxLineLen,D0
            BEQ NoNeed                      * Branch if so.
            MOVE.B D0,NewCrsrXCo            * Move cursor to end of text line.
            JSR CrsrMvAc
            JSR WipeLine                    * Clear remainder of line.
NoNeed:     MOVE.B CursorXPos,NewCrsrXCo
            JSR CrsrMvAc
            CLR.L D0
            MOVE.B CursorXPos,D0
            ADD.L TextBuff,D0
            MOVE.L D0,SrcPtr
            JSR PrntStr
            MOVE.B (sp)+,Line               * Move cursor back to original position.
            MOVE.B (sp)+,Column
            JSR MoveCurs
            RTS


* SUBROUTINE UpdateLine. This routine updates a text line on the screen. The current cursor position before the call is preserved.

UpdateLine: MOVE.B CursorXPos,-(sp)         * Save current cursor co-ords.
            MOVE.B CursorYPos,-(sp)
            CLR.B Column                    * Move cursor to start of line.
            MOVE.B BuffStY,Line
            JSR MoveCurs
            MOVE.L TextBuff,SrcPtr          * Set pointer to start of text line.
            MOVE.B MaxLineLen,LineLen       * Set length of text line and display the line...
            JSR PrintLine
            MOVE.B (sp)+,Line               * Move cursor back to original position.
            MOVE.B (sp)+,Column
            JSR MoveCurs
            RTS


* SUBROUTINE UpdateLn. This routine updates a text line on the screen. LineNumber must contain the line to update.
*                      Position must contain the place on the line from which to update the line.


UpdateLn:   JSR GetLineYCo                  * Get y co-ordinate of text line.
            MOVE.B D0,Line                  * Update the line on screen...
            MOVE.B D0,-(sp)                 * (save y co)
            MOVE.B BuffLength,Column
            JSR MoveCurs
            JSR WipeLine
            MOVE.B Position,Column
            MOVE.B (sp)+,Line
            JSR MoveCurs
            CLR.L D1
            MOVE.B Position,D1
            MOVE.W LineNumber,D0
            JSR GetLineAdr
            ADD.L D1,D0
            MOVE.L D0,SrcPtr
            JSR PrntStr
            RTS


* SUBROUTINE PrintLine. This routine displays the contents of a text buffer at the current cursor position.
*                       Unlike the GEMDOS version, this will not terminate at the discovery of a zero byte,
*                       but when a specified number of characters have been displayed.
*                       SrcPtr must contain the start of the text line.
*                       LineLen must contain the number of characters to be displayed.
*                       Any zero bytes are displayed as spaces.

PrintLine:  MOVEA.L SrcPtr,A2               * Set pointer to start of line to be displayed.
            CLR.L D2                        * Calculate loop termination address...
            MOVE.B LineLen,D2
            ADD.L A2,D2
PrntLnLoop: CMP.L A2,D2                     * End of line reached yet?
            BEQ PrntLnEnd                   * Branch if yes.
            MOVE.B (A2)+,SrcChar            * Otherwise, transfer next character. But is it a zero byte?
            BNE NotAZero                    * Branch if no.
            MOVE.B #' ',SrcChar             * Otherwise, change the zero byte to a space.
NotAZero:   JSR PrintChar                   * Send character to screen.
            BRA PrntLnLoop
PrntLnEnd:  RTS


* SUBROUTINE PrintChar. This routine sends one character to the screen at the current cursor position.
*                       The ASCII code of the character to be sent must be in SrcChar.

PrintChar:  CLR.W -(sp)                     * Make space for character code on stack.
            MOVE.B SrcChar,1(sp)            * Transfer code.
            MOVE.W #ConOut,-(sp)            * Function number for GEMDOS Conout
            TRAP #GemDos                    * Send the character to the screen.
            ADDQ #4,sp                      * CS
            RTS


* SUBROUTINE EnterLine. This routine enters a line of text, storing it at TextBuff.

EnterLine:  MOVEM.L A0-A6/D0-D7,-(sp)       * SRTBU
            MOVE.B MaxCrsrXCo,-(sp)         * Save current maximum cursor position.
            CLR.B QuitEnter                 * Return or Esc halts this routine. QuitEnter set to Yes when either is pressed.
            MOVE.B #No,Cancelled            * If Esc is pressed, Cancelled is set to Yes for the outside program's use.
            MOVE.L TextBuff,SrcPtr          * Find length of text buffer...
            JSR FindBufLen
            CLR.L D0                        * Calculate MaxAddress which Copydata2 needs to prevent overflow when characters are
            MOVE.B MaxLineLen,D0            * inserted into the text line...
            MOVE.B D0,MaxCrsrXCo            * (find maximum position of cursor at the same time)
            SUBQ.B #1,MaxCrsrXCo
            ADD.L TextBuff,D0
            MOVE.L D0,MaxAddress
            DC.W HideMouse                  * Turn mouse sprite & cursor off as screen is changing.
            JSR CursrOff
            JSR UpdateMesg                  * Update insert/replace mode message.
            MOVE.B BuffStY,NewCrsrYCo       * If cursor is ever moved, y-co is always the same.
            MOVE.B BuffStY,Line             * Move cursor to start of line:
            CLR.B Column
            JSR MoveCurs
            MOVE.L TextBuff,SrcPtr          * Set SrcPtr & LineLen for PrintLine to diplay initial state on screen...
            MOVE.B MaxLineLen,LineLen
            JSR PrintLine                   * Line now shown.
            CMPI.B #Yes,IgnoreOld           * If EnterLine gets called again from an 'Alter path/file name' request, this prevents
            BEQ Continue                    * the old path/file name from corrupting the one the user wishes to alter.
            CMPI.B #No,UseOld               * Is an old line to be used?
            BEQ Continue                    * Branch if no.
            JSR RestoreOld                  * Transfer old line to line buffer.
Continue:   MOVE.B BuffStY,TxtBlkMin        * Text block is just one line...
            MOVE.B BuffStY,TxtBlkMax
            CLR.B TxtBlkLeft                * Set left hand side of line. NB. These TxtBlk... variables are used by MwsOutside.
            MOVE.B #79,TxtBlkRyte           * Set right hand side of line.
            MOVE.B BuffLength,NewCrsrXCo    * Move cursor to end of line.
            JSR CrsrMvAc
            JSR CursorOn                    * Turn cursor & mouse sprite back on, as screen is ready...
            DC.W ShowMouse
            MOVE.B #No,ScrnChange           * Initialise flag. This is set every time the line's contents changes.
NextScan:   CMPI.B #No,ScrnChange           * Any change on screen?
            BEQ NoUpdate                    * Branch if no.
            JSR CursrOff
            JSR UpdateScrn                  * Update the line on the screen.
            MOVE.B #No,ScrnChange           * Clear flag.
NoUpdate:   JSR CrsOnMwsOn                  * Turn on mouse and cursor if necessary.
            CLR.B ActionCode                * Clear action code.
            CLR.B KeybrdStat                * Clear status of special keys.
            MOVE.B #No,MwsFlag              * Clear mouse-cursor-movement flag.
            JSR GetCursPos                  * Get current cursor position.
            JSR GetMsPos                    * Get info on mouse position, button states, e.t.c..
            TST.B MwsChange                 * Has anything with the mouse changed/happened?
            BEQ NoMouseOp                   * Branch if no.
            JSR MouseOn                     * Otherwise, turn on mouse.
            MOVE.B #Yes,MwsAcFlag           * Set flag to show that the situation with the mouse has changed.
            TST.B ButChange                 * Has either button been pressed?
            BEQ NoMouseOp                   * Branch if no.
            JSR MwsOutside                  * Find out if mouse cross-hair cursor is on the text line.
            CMPI.B #No,MwsInside            * Is the mouse cursor on the line?
            BEQ NoMouseOp                   * Branch if no.
            TST.B RightBut                  * Was it the right button that was pressed?
            BEQ SetCrsrPos                  * Branch if no.
            JSR RyteButAc                   * Otherwise, set code to denote whether delete/insert a word is to happen.
            CMPI.B #Yes,MwsFlag             * Is the operation just a cursor movement?
            BEQ SetCrsrPos                  * Branch if yes.
            CMPI.B #UndoDWCode,ActionCode   * Is it an InsertWord action?
            BEQ InsrtWrd                    * Branch if yes.
            CMPI.B #DelWordCode,ActionCode  * Is it a DeleteWord action?
            BNE SetCrsrPos                  * If no, branch as the line must be empty.
            DC.W HideMouse
            JSR CursrOff
            MOVE.B BuffLength,D7            * If cursor is at end of line, it must be moved left one first...
            CMP.B CursorXPos,D7             * Is this the case?
            BNE CursrPosOk                  * Branch if no.
            SUBQ.B #1,CursorXPos            * Otherwise, move cursor left one (memory only!).
CursrPosOk: MOVE.B CursorXPos,Position      * Set start scan position for DeleteWord...
            JSR DeleteWord                  * ...and the word (if there is one) is gone.
            MOVE.B BuffLength,NewCrsrXCo    * Update the line on screen...
            JSR CrsrMvAc
            JSR WipeLine
LineUpdt:   MOVE.B Position,NewCrsrXCo
            JSR CrsrMvAc
            CLR.L D0
            MOVE.B Position,D0
            ADD.L TextBuff,D0
            MOVE.L D0,SrcPtr
            JSR PrntStr
            BRA NewCrsrPos                  * Jump to re-show cursor, e.t.c.
InsrtWrd:   DC.W HideMouse
            JSR CursrOff
            JSR InsertWord                  * Old word inserted.
            BRA LineUpdt                    * Branch to update line.
SetCrsrPos: MOVE.B MwsChrXPos,NewCrsrXCo    * Get new legal cursor x co-ordinate...
            JSR GetOkXCo
CrsrMoveVc: MOVE.B NewCrsrXCo,D1
            CMP.B CursorXPos,D1             * No need to move cursor if it's already there. Is the x-co different?
            BNE GoNewCrsPs                  * Branch if yes.
            BRA NextScan                    * Otherwise, cursor postition is the same, so do nothing.
GoNewCrsPs: DC.W HideMouse
            JSR CursrOff
NewCrsrPos: JSR CrsrMvAc
            BRA NextScan
NoMouseOp:  JSR ScanKeybrd                  * Scan the keyboard.
            TST.B ScanCode                  * Has a key been pressed?
            BEQ NextScan                    * Branch if no.
LftArrwTst: CMPI.B #LftCrsrKey,ScanCode     * Has left arrow been pressed?
            BNE RtArrowTst                  * Branch if no.
            JSR IsLftArrOk                  * Check to see if action is ok.
            CMPI.B #No,YNAnswer             * Is action allowed?
            BEQ NextScan                    * Branch if no.
            JSR CrsrLeftAc                  * Set new cursor position/Set action code if Shift pressed.
            CMPI.B #LeftWrdCode,ActionCode  * Is it a move-one-word-left action?
            BNE CrsrMoveVc                  * Branch if no.
            JSR WordLeft                    * Move the cursor to new position one word leftwards.
            BRA CrsrMoveVc
RtArrowTst: CMPI.B #RtCrsrKey,ScanCode      * Has right arrow been pressed?
            BNE LnStartTst                  * Branch if no.
            JSR IsRyteArOk                  * Check to see if action is ok.
            CMPI.B #No,YNAnswer             * Is action allowed?
            BEQ NextScan                    * Branch if no.
            JSR CrsrRyteAc                  * Set new cursor position/Set action code if Shift pressed.
            CMPI.B #RyteWrdCode,ActionCode  * Is it a move-one-word-right action?
            BNE CrsrMoveVc                  * Branch if no.
            JSR WordRight                   * Move the cursor to new position one word rightwards.
            BRA CrsrMoveVc
LnStartTst: CMPI.B #LnStartKey,ScanCode     * Has Control + Left cursor key been pressed? (for move one word left)
            BNE LineEndTst                  * Branch if no.
            TST.B CursorXPos                * Is cursor already at start of line?
            BEQ NextScan                    * Branch if yes.
            DC.W HideMouse                  * Otherwise, mouse off.
            CLR.B NewCrsrXCo                * Set new cursor position to be at start of line.
            BRA NewCrsrPos
LineEndTst: CMPI.B #LineEndKey,ScanCode     * Has Control + Right cursor key been pressed? (for move one word right)
            BNE DeleteTest                  * Branch if no.
            MOVE.B BuffLength,D0            * Is cursor already at end of line?
            CMP.B CursorXPos,D0
            BEQ NextScan                    * Branch if yes.
            MOVE.B MaxCrsrXCo,D0            * But is the cursor already at its maximum position?
            CMP.B CursorXPos,D0
            BEQ NextScan                    * Branch if yes.
            DC.W HideMouse                  * Othwerwise, mouse off.
            MOVE.B BuffLength,NewCrsrXCo    * Set new cursor position to be at start of line.
            BRA NewCrsrPos
DeleteTest: CMPI.B #DeleteKey,ScanCode      * Has Delete key been pressed?
            BNE BackspcTst                  * Branch if no.
            MOVE.B BuffLength,D0            * Is cursor at end of line (if so, nothing to delete)?
            CMP.B CursorXPos,D0
            BEQ NextScan                    * Branch if yes.
            DC.W HideMouse
            JSR CursrOff
            JSR DeleteAc                    * Character deleted.
            MOVE.B CursorXPos,NewCrsrXCo
            MOVE.B #Yes,ScrnChange
            BRA CrsrMoveVc
BackspcTst: CMPI.B #BackspcKey,ScanCode     * Has Backspace been pressed?
            BNE InsertTest                  * Branch if no.
            TST.B CursorXPos                * Is cursor at start of line (if so, Backspace wouldn't do anything)?
            BEQ NextScan                    * Branch if yes.
            DC.W HideMouse
            JSR BackspcAc
            MOVE.B #Yes,ScrnChange
            BRA CrsrMoveVc
InsertTest: CMPI.B #InsertKey,ScanCode      * Has Insert key been pressed?
            BNE ClrHomeTst                  * Branch if no.
            DC.W HideMouse
            JSR InsertAc
            BRA NextScan
ClrHomeTst: CMPI.B #ClrHomeKey,ScanCode     * Has Clr Home been pressed?
            BNE UndoTest                    * Branch if no.
            CMPI.B #Yes,TempBuffSt          * Does the temporary buffer contain anything?
            BEQ NextScan                    * Branch if yes.
            DC.W HideMouse
            JSR ClrHomeAc
            MOVE.B #Yes,ScrnChange
            BRA NewCrsrPos
UndoTest:   CMPI.B #UndoKey,ScanCode        * Has Undo been pressed?
            BNE RtrnKeyTst                  * Branch if no.
            BTST.B #AltBit,KeybrdStat       * Has Alternate been pressed as well?
            BEQ UndoClrHm                   * Branch if no.
            CMPI.B #No,UseOld               * Request to restore old line. Is there one?
            BEQ NextScan                    * Branch if no.
            DC.W HideMouse                  * Otherwise, restore original old line...
            JSR CursrOff
            JSR RestoreOld
            BRA NextScan
UndoClrHm:  CMPI.B #No,TempBuffSt           * Is there anything in the temporary buffer?
            BEQ NextScan                    * Branch if no.
            DC.W HideMouse
            JSR CursrOff
            JSR UndoAc
            MOVE.B #Yes,ScrnChange
            BRA NewCrsrPos
RtrnKeyTst: CMPI.B #ReturnKey,ScanCode      * Has the Return key been pressed?
            BNE EscKeyTest                  * Branch if no.
            BRA EntrLnEnd
EscKeyTest: CMPI.B #EscKey,ScanCode         * Has the Esc key been pressed?
            BNE CharTest                    * Branch if no.
            JSR EscAc
            BRA EntrLnEnd
CharTest:   CMPI.B #32,NextChar             * Is there a valid ASCII code present?
            BLT NextScan                    * Branch if no (this prevents control codes).
            DC.W HideMouse
            JSR CursrOff
            JSR CharAc
            MOVE.B CursorXPos,NewCrsrXCo    * Move cursor on one position.
            ADDQ.B #1,NewCrsrXCo
            MOVE.B #Yes,ScrnChange
            BRA NewCrsrPos
EntrLnEnd:  DC.W HideMouse
            JSR CursrOff
            JSR ClearMess                   * Blank out Insert/Replace message.
            JSR ClrEntrLn                   * Blank out text line.
            MOVE.B (sp)+,MaxCrsrXCo         * Restore original maxium cursor position.
            MOVEM.L (sp)+,A0-A6/D0-D7       * RRU
            RTS


* The following 2 routines are the check routines used by EnterLine (AND the main editor!) to test whether the actions wanted are
* viable. The returning answer is always either Yes or No in YNAnswer.

* This routine tests whether a left cursor key action is allowed...

IsLftArrOk: MOVE.B #Yes,YNAnswer            * Assume answer is Yes.
            TST.B CursorXPos                * Is cursor at start of line?
            BNE LftArFlgOk                  * Branch if no - flag is ok.
            MOVE.B #No,YNAnswer             * Otherwise, switch flag.
LftArFlgOk: RTS

* This routine tests whether a right cursor key action is allowed...

IsRyteArOk: MOVE.B #Yes,YNAnswer            * Assume answer is Yes.
            MOVE.B BuffLength,D4            * Is cursor at end of line?
            CMP.B CursorXPos,D4
            BNE RytArFlgOk                  * Branch if no - flag is ok.
            MOVE.B #No,YNAnswer             * Otherwise, switch flag.
RytArFlgOk: RTS
            

* The following routines are the various actions that can be called by EnterLine and, if compatible, by the main file editor.

* This routine is called when the left cursor key is pressed and allowed...

CrsrLeftAc: BTST.B #EthrShftBit,KeybrdStat  * Was either Shift key pressed?
            BEQ CrsrLeft1                   * Branch if no.
            MOVE.B #LeftWrdCode,ActionCode  * Otherwise, set code for moving one word left.
            RTS
CrsrLeft1:  MOVE.B CursorXPos,NewCrsrXCo    * Transfer current cursor position.
            SUBQ.B #1,NewCrsrXCo            * Set new position one to the left.
            RTS


* This routine is called when the right cursor key is pressed and allowed...

CrsrRyteAc: BTST.B #EthrShftBit,KeybrdStat  * Was either Shift key pressed?
            BEQ CrsrRyte1                   * Branch if no.
            MOVE.B #RyteWrdCode,ActionCode  * Otherwise, set code for moving one word right.
            RTS
CrsrRyte1:  MOVE.B CursorXPos,NewCrsrXCo    * Transfer current cursor position.
            ADDQ.B #1,NewCrsrXCo            * Set to new position. NB CrsrMvAc ensures CursorXPos doesn't equal MaxLineLen.
            RTS


* CrsrMvAc is called when a cursor movement is required. It ensures that the new cursor position does not go out of range.
* MaxCrsrXCo must contain the maximum cursor x position allowed (thus cursor set <= MaxCrsrXCo).
* NewCrsrXCo must contain the intended new X position, even if out of range.
* NewCrsrYCo must contain the intended new Y position.

CrsrMvAc:   MOVE.B MaxCrsrXCo,D2            * Must not let cursor become greater than or equal to maximum line length.
            CMP.B NewCrsrXCo,D2             * Is the cursor position o.k.?
            BCC PosOk                       * Branch if yes.
            MOVE.B D2,NewCrsrXCo            * Otherwise, set position to last character possible.
PosOk:      MOVE.B NewCrsrXCo,Column        * go off the edge of the screen when MaxLineLen = 80. Sneaky!
            MOVE.B NewCrsrYCo,Line          * New co-ordinates transfered...
            JSR MoveCurs                    * ...so move the cursor to the new position.
            RTS


* DeleteAc may be called when the Delete key is pressed.
* TextBuff must point to the start of the text line.
* CursorXPos must point to the character in the line to be deleted.

DeleteAc:   MOVE.B CursorXPos,Position      * Set variables necessary for DeleteData...
            MOVE.B #1,NumChars
            JSR DeleteData                  * Unwanted character has gone.
            RTS


* BackspcAc may be called when the Backspace key is pressed.

BackspcAc:  SUBQ.B #1,CursorXPos            * BackspcAc is really like DeleteAc working one position to the left, so decrease
            JSR DeleteAc                    * the cursor position and call DeleteAc!
            MOVE.B CursorXPos,NewCrsrXCo    * Set new x position...
            JSR CrsrMvAc
            RTS


* InsertAc will be called whenever the Insert key is pressed. It toggles the entering mode between Insert & Replace.

InsertAc:   MOVE.B #No,D2                   * Although 'NOT.B InsrtState' would have worked, doing it this way allows Yes & No to
            CMPI.B #Yes,InsrtState          * take on any value.
            BEQ Switch
            MOVE.B #Yes,D2
Switch:     MOVE.B D2,InsrtState
            JSR UpdateMesg                  * Update the screen message (if there is one).
            RTS


* ClrHomeAc may be called when the ClrHome key is pressed.

ClrHomeAc:  MOVE.B BuffLength,TmpBuffLen    * Save length of text line.
            MOVE.B CursorXPos,TmpCrsrXCo    * Save current cursor position on text line.
            MOVE.L TextBuff,SrcPtr          * Set source pointer to text line.
            MOVE.L #TempBuff,DstPtr         * Set destination pointer to temporary buffer.
            MOVE.B MaxLineLen,LineLen       * Set number of characters to copy.
            JSR CopyData                    * Copy text line to temporary buffer.
            JSR ClearData                   * Clear current text line.
            CLR.B BuffLength                * Nothing there now.
            CLR.B NewCrsrXCo                * Move cursor to start of empty line.
            MOVE.B #Yes,TempBuffSt          * Set flag to indicate temporary buffer contains something.
            RTS


* SUBROUTINE SaveTmpBf. This routine saves the state of the temporary EnterLine buffer.

SaveTmpBf:  MOVE.B TempBuffSt,SavedTBS      * Save EnterLine buffer variables...
            MOVE.B TmpBuffLen,SavedTBL
            MOVE.B TmpCrsrXCo,SavedTCX
            MOVE.L #TempBuff,SrcPtr
            MOVE.L #LineWork,DstPtr
            MOVE.B MaxLineLen,LineLen
            JSR CopyData
            RTS

* SUBROUTINE RestrTmpBf. This routine restores the state of the temporary EnterLine buffer.

RestrTmpBf: MOVE.B SavedTBS,TempBuffSt      * Restore EnterLine buffer variables...
            MOVE.B SavedTBL,TmpBuffLen
            MOVE.B SavedTCX,TmpCrsrXCo
            MOVE.L #LineWork,SrcPtr
            MOVE.L #TempBuff,DstPtr
            MOVE.B MaxLineLen,LineLen
            JSR CopyData
            RTS


* UndoAc may be called when the Undo key is pressed.

UndoAc:     MOVE.B TmpBuffLen,BuffLength    * Restore saved buffer length.
            MOVE.L #TempBuff,SrcPtr         * Set source pointer to temporary buffer.
            MOVE.L TextBuff,DstPtr          * Set destination pointer to text line.
            MOVE.B MaxLineLen,LineLen       * Set number of characeters to copy.
            JSR CopyData                    * Copy temporary buffer to text line.
            JSR ClearData                   * Clear temporary buffer.
            MOVE.B TmpCrsrXCo,NewCrsrXCo    * Restore old position of cursor.
            MOVE.B #No,TempBuffSt           * Set flag to indicate buffer is empty.
            JSR UpdateLine
            RTS


* EscAc is called if the user presses the ESC key. This will cancel the EnterLine function. If ?UseOld is true,
* the old line is returned, despite any changes made to TextBuff. If ?UseOld = No, an empty line is returned.
* This is the 2nd posssible termination route of EnterLine.

EscAc:      MOVE.B MaxLineLen,LineLen       * Blank out text line (since ESC represents a canceling of the function).
            MOVE.L TextBuff,SrcPtr
            JSR ClearData
            CLR.B BuffLength                * Text buffer now empty.
            CMPI.B #No,UseOld               * Restore an old line?
            BEQ EscKeyEnd                   * Branch if no.
            JSR RestoreOld                  * Transfer old line to text line.
EscKeyEnd:  MOVE.B #Yes,QuitEnter           * Set flag to terminate EnterLine.
            MOVE.B #Yes,Cancelled           * Set flag to denote that user has cancelled the EnterLine function.
            RTS


* CharAc may be called if a valid ASCII character code is present in NextChar.
* There are really two seperate routines here: ReplcStart is the routine used when in Replace mode.
*                                              InsrtStart is the routine used when in Insert mode.
* There are certain cases where ReplcStart always used , e.g. when the cursor is at the end of the text line, or is on the last
* character of a totally full line.

CharAc:     MOVEM.L D4-D6/A4,-(sp)          * SRTBU
            MOVEA.L TextBuff,A4             * Set pointer to start of text buffer.
            CLR.L D5                        * BuffLength & CursorXPos are used often. Copying them to registers will save time.
            CLR.L D6
            MOVE.B BuffLength,D5
            MOVE.B D5,D4                    * Copy needed as original wiped out if in Insert mode.
            MOVE.B CursorXPos,D6            * Any operation at the end of a line is the same as a Replace operation...
            CMP.B MaxCrsrXCo,D6
            BEQ ReplcStart                  * So branch if this is so.
            CMP.B D6,D5                     * Otherwise, is cursor after the last character on the line?
            BEQ IncBuffLen                  * Branch if yes, place character.
            CMPI.B #No,InsrtState           * Is Replace mode On?
            BEQ ReplcStart                  * Branch if yes.
InsrtStart: MOVE.L A4,SrcPtr                * Calculate source and destination addresses for CopyData2...
            ADD.L D5,SrcPtr            
            MOVE.L SrcPtr,DstPtr
            ADDQ.L #1,DstPtr
            SUB.B D6,D5                     * Calculate number of characters to copy...
            MOVE.B D5,LineLen
            JSR CopyData2
            CMP.B MaxLineLen,D4             * Is the text line full?
            BEQ PutCharIn                   * Branch if yes.
            BRA IncBuffLen                  * Otherwise, jump to increase length of text buffer.
ReplcStart: CMP.B D6,D4                     * Does the buffer length need increasing?
            BNE PutCharIn                   * Branch if no.
IncBuffLen: ADDQ.B #1,BuffLength            * Increase buffer length.
PutCharIn:  MOVE.B NextChar,0(A4,D6.W)      * Put character into text line at cursor position.
            MOVEM.L (sp)+,D4-D6/A4          * RRU
            RTS


* ActionCode:- LftWrdCode.
* This routine finds the beginning of the next word leftwords in a line of text. The same parameters are needed
* for NxtWrdRyte.
* TextBuff must point to the start of the text line.
* LineLen must contain the current length of the line.
* Position must contain the place where the search routine is to begin.

NxtWrdLeft: MOVEM.L D4-D6/A4,-(sp)              * SRTBU
            CLR.W D5                            * If the search started on a space at the end of a line, the checks below are
            MOVE.B Position,D5                  * not needed - so save character at end of line for later check.
            MOVEA.L TextBuff,A4
            MOVE.B BuffLength,D4
            CLR.B D6
            CMP.B D5,D4
            BNE NotOnLast
            MOVE.B -1(A4,D5.W),D6               * (character saved in D6)
NotOnLast:  JSR NxtSpcLeft
            CMPI.B #32,D6                       * Must skip check if search started on space.
            BEQ NoCheck
            TST.B Position                      * If start position is at end of line, only NxtSpcLeft, NxtChrRyte are needed.
            BEQ WrdLeftEnd                      * Branch if yes.
            CMP.B D5,D4                         * Was the original search start position at the end of the line?
            BEQ FindChrRyt                      * If so, branch - as only these two are then necessary.
            SUB.B Position,D5                   * Calculate change in position. If change > 1, search started in the middle of a
            CMPI.B #1,D5                        * word, so branch in this case as well.
            BGT FindChrRyt
NoCheck:    JSR NxtChrLeft
            TST.B Position                      * Start of line reached?
            BEQ WrdLeftEnd                      * Branch if yes.
            JSR NxtSpcLeft
            TST.B Position                      * Start of line reached?
            BEQ WrdLeftEnd                      * Branch if yes.
FindChrRyt: JSR NxtChrRyte
WrdLeftEnd: MOVEM.L (sp)+,D4-D6/A4              * RRU
            RTS


* ActionCode RytWrdCode.
* This routine finds the start of the next word rightwards in a text line.
* The routine needs the same paramaters as NxtWrdLeft.

NxtWrdRyte: JSR NxtSpcRyte
            JSR NxtChrRyte
            RTS


* This routine deletes a word at Position in a text line.
* If Position points to a space, the size of the block of spaces present is evaluated. If this is more than one,
* the block is reduced to just one space. Otherwise, the word following (if any) and the space are deleted.
* TextBuff must point to the start of the text line,
* Position must contain the initial scan start position (offset from TextBuff, not an address!),
* BuffLength must contain the number of characters in the line.

DeleteWord: MOVEM.L D4-D7/A4,-(sp)              * SRTBU
            MOVEA.L TextBuff,A4                 * Must find out if the cursor is on a space...
            CLR.W D4
            MOVE.B Position,D4
            CMPI.B #32,0(A4,D4.W)               * Well? Is there a space there?
            BNE NoSpcBlock                      * Branch if no.
            JSR NxtChrLeft                      * Otherwise, find the size of the block of spaces Position is pointing to...
            JSR NxtSpcRyte
            MOVE.B Position,D6
            MOVE.B D4,Position
            JSR NxtChrRyte
            MOVE.B Position,D5
            MOVE.B D5,D7
            SUB.B D6,D5                         * If the number of spaces (?D5.B) <= 1 then delete word at Position...
            CMP.B BuffLength,D7                 * If the block of spaces is found at the end of a line, all will be deleted,
            BEQ DelAllSpcs                      * no matter how many (branch if this is so).
            CMPI.B #1,D5
            BLE NoSpcBlock                      * Branch if less than 2 spaces.
            SUBQ.B #1,D5                        * Otherwise, delete all the spaces in the block except one...
DelAllSpcs: MOVE.B D6,Position
            BRA GoDeletion
NoSpcBlock: JSR NxtSpcRyte                      * RemoveData will be used to delete the word - therefore, calculate the number of
            MOVE.B Position,D5                  * characters to remove...
            MOVE.B D4,Position
            JSR NxtSpcLeft                      * Position now ready for RemoveData.
            SUB.B Position,D5
GoDeletion: MOVE.B D5,NumChars
            JSR RemoveData                      * Word removed.
DelWordEnd: MOVEM.L (sp)+,D4-D7/A4              * RRU
            RTS


* This routine is the opposite of DeleteWord. It inserts the last word stored by DeleteWord.
* Same parameters as DeleteWord (except CursorXPos is taken as the insertion position).

InsertWord: MOVE.B CursorXPos,D1                * First find the next space left (not done if at end of line)...
            MOVE.B D1,Position
            CMP.B BuffLength,D1
            BEQ CursPosOk           
            JSR NxtSpcLeft
CursPosOk:  JSR InsertData                      * Then the word is inserted.
            RTS

* End of EnterLine ActionCode routines.


* This routine is called by the mouse button test if the right button has been pressed.

RyteButAc:  MOVE.B #Yes,MwsFlag             * Set flag indicating cursor movement.
            MOVE.B MwsChrXPos,D1            * If equivalent cursor position is past end of line, set to end of line...
            CMP.B BuffLength,D1
            BLE MwsPosOk
            MOVE.B BuffLength,D1
MwsPosOk:   MOVE.B D1,NewCrsrXCo            * Set new cursor position.
            JSR SpshlKyScn                  * Scan Shift keys.
            BTST.B #EthrShftBit,KeybrdStat  * Is either Shift key being pressed as well?
            BEQ NoShftKey                   * Branch if no (mouse right button on its own is delete a word).
            TST.B WrdStrLen                 * Insert word: Nothing would happen if the word store is 'empty', so - is it?
            BEQ RyteButEnd                  * If yes, just treat as a cursor movement, so branch.
            MOVE.B #UndoDWCode,ActionCode   * Otherwise, set code for undoing delete-a-word.
            MOVE.B NewCrsrXCo,CursorXPos    * If a word is going to be inserted, set cursor position.
            BRA ClearFlag
NoShftKey:  JSR NoButton                    * Mouse scanning is fast, so wait until button is released.
            TST.B BuffLength                * Nothing would happen if line is empty, so - is it?
            BEQ ClearFlag                   * Branch if yes.
            MOVE.B #DelWordCode,ActionCode  * Otherwise, set code for DeleteWord action.
            MOVE.B NewCrsrXCo,CursorXPos    * If a word is going to be deleted, set cursor position.
ClearFlag:  MOVE.B #No,MwsFlag
RyteButEnd: RTS


* SUBROUTINE NoButton. This routine waits until neither of the mouse buttons is being pressed.

NoButton:   JSR GetMsPos
            TST.B ButChange
            BNE NoButton
            RTS


* SUBROUTINE ScanKeybrd. This routine scans the keyboard for a key press. The routine uses BIOS Bconin to scan the keyboard.
*                        If a key was pressed:- ScanCode contains the scan code of the key,
*                                               NextChar contains the ASCII code of the key (if it has one),
*                        KeybrdStat contains the states of the special keys (e.g. ALT) in the following way:-
*                        The meaning of KeybrdStat's contents is the same as a BIOS Kbshift function call (Kbshift doesn't use
*                        bit 7, so the program uses this to denote if EITHER shift key has been pressed), i.e.

*                        Bit No.       Key       |  Bit No.       Key
*                                                |
*                           0       Right Shift  |     4      Caps Lock       
*                           1       Left Shift   |     5      Right Mouse button
*                           2       Control      |     6      Left Mouse button
*                           3       Alternate    |     7      EITHER Shift key (normally unused)

*                        If one of the involved keys is pressed, the appropriate bit is set.

ScanKeybrd: MOVE.W #2,-(sp)                 * Must see if there is a character available, so call Bconstat first.
            MOVE.W #BConstat,-(sp)          * Bconstat code.
            TRAP #Bios
            ADDQ.W #4,sp                    * CS
            TST.L D0                        * Character available?
            BNE KeyPressed                  * Branch if yes.
            CLR.B NextChar                  * Otherwise, clear key code variables...
            CLR.B ScanCode
            BRA ScanEnd
KeyPressed: MOVE.W #2,-(sp)                 * Device number 2 is the keyboard.
            MOVE.W #BConin,-(sp)            * Code for Bconin.
            TRAP #Bios                      * Call function
            ADDQ.W #4,sp                    * CS
            MOVE.B D0,NextChar              * Save ASCII code of key pressed (0 if none pressed).
            SWAP D0                         * Make scan code accesible.
            MOVE.B D0,ScanCode              * Save scan code of key pressed (0 if none pressed).
            LSR.W #8,D0                     * Get equivalent of Kbshift result in low byte.
            MOVE.B D0,KeybrdStat            * Save this information.
            ANDI.B #3,D0                    * Set flag to denote whether either of the two shift keys have been pressed.
            BEQ ScanEnd
            BSET.B #EthrShftBit,KeybrdStat  * Sneaky! Bit 7 is normally unused, so use this as the flag to denote bit 0 OR bit 1.
ScanEnd:    RTS


* SUBROUTINE SpshlKyScn. This routine scans the special keys (Alt, Control, e.t.c.) and sets the bit vector KeybrdStat in the
*                        same way KeybrdScan does.

SpshlKyScn: CLR.L -(sp)                     * The user stack becomes the Supervisor stack.
            MOVE.W #$20,-(sp)               * Code for calling SUPER function onto stack.
            TRAP #GemDos                    * Supervisor mode is active after the TRAP call.
            ADDQ.W #6,sp                    * CS
            MOVE.L D0,SaveSSP               * D0 = old supervisor stack, so save!!!
            MOVE.L A4,-(sp)                 * SRTBU
            MOVE.L KbshiftLoc,A4            * Get Kbshift special key status and store in KeybrdStat...
            MOVE.B (A4),KeybrdStat
            MOVE.L (sp)+,A4
            MOVE.L SaveSSP,-(sp)            * Old supervisor stack pointer on stack.
            MOVE.W #$20,-(sp)               * Code for calling SUPER function onto stack.
            TRAP #GemDos                    * Back in User mode after the TRAP instruction.
            ADDQ.W #6,sp                    * CS
            MOVE.B KeybrdStat,D0            * Set flag to denote whether either of the two shift keys have been pressed...
            ANDI.B #3,D0
            BEQ NoShiftKey
            BSET.B #EthrShftBit,KeybrdStat  * Bit 7 is normally unused, so use this as the flag to denote bit 0 OR bit 1.
            BRA SpshlKyEnd
NoShiftKey: BCLR.B #EthrShftBit,KeybrdStat  * No key Shift key pressed, so clear flag.
SpshlKyEnd: RTS


* SUBROUTINE ShowSpeshl. This routine allows the characters denoting control codes to be shown on screen.
*                        The ASCII code of the character to be sent must be in SrcChar.

ShowSpeshl: CLR.W -(sp)                     * Make space for character code on stack.
            MOVE.B SrcChar,1(sp)            * Transfer code.
            MOVE.W #GoCntrlCds,-(sp)        * Device number for Bconout allowing control codes to be displayed.
            MOVE.W #Bconout,-(sp)           * Function number for BIOS Bconout.
            TRAP #Bios                      * Send the character to the screen.
            ADDQ.W #6,sp                    * CS
            RTS


* SUBROUTINE PrntStr. This routine outputs a string on the screen at the current cursor position.
*                     SrcPtr must contain the start address of the string.

PrntStr:    MOVE.L SrcPtr,-(sp)             * Start address of string to be displayed.
            BRA SendLine                    * (****) Jump to routine which executes a GEMDOS PrintLine command.


* SUBROUTINE MwsCrsrOn. This routine turns the cursor on if it is not already on, and makes the mouse sprite visible if the mouse
*                       has done anything (in which case, MwsAcFlag is set).

CrsOnMwsOn: DC.W InitA                      * Is the cursor currently on?
            TST.W -346(A0)
            BEQ AlreadyOn                   * Branch if yes.
            JSR CursorOn
AlreadyOn:  CMPI.B #Yes,MwsAcFlag           * Has the mouse done anything?
            BEQ TurnOnMws                   * Branch if yes.
            RTS
TurnOnMws:  JSR MouseOn
            MOVE.B #No,MwsAcFlag
            RTS


* SUBROUTINE CursorOn. This routine turns the cursor on.

CursorOn:   MOVE.L #CursOn,-(sp)            * Address of enable cursor command string onto stack.
            BRA SendLine                    * See note (****) 


* SUBROUTINE CursrOff. This routine turns the cursor off.
 
CursrOff:   MOVE.L #CursOff,-(sp)           * Address of disable cursor command string onto stack.
            BRA SendLine                    * See note (****)


* SUBROUTINE ReverseOn. This routine makes all further character output appear inverted (black on white background).

ReverseOn:  MOVE.L #RevrsOn,-(sp)           * Address of reverse mode on command sequence on stack...
            BRA SendLine


* SUBROUTINE ReverseOff. This routine turns off the inverse character output activated by ReverseOn.

ReverseOff: MOVE.L #RevrsOff,-(sp)          * Address of reverse mode on command sequence on stack...
            BRA SendLine


* SUBROUTINE WipeScrn.  This routine clears the screen.

WipeScrn:   MOVE.L #ClrScrn,-(sp)           * Place address of clear screen command sequence on stack...
            BRA SendLine                    * See note (****)


* SUBROUTINE WipeLine. This routine clears the rest of the line from the current cursor position (inclusive).

WipeLine:   MOVE.L #ClrRemLine,-(sp)        * Place address of clear remainder of line command sequence on stack.
            BRA SendLine                    * See note (****)


* SUBROUTINE ClrTxtWndw. This routines clears the whole of the text display area beneath the text partition bar.

ClrTxtWndw: MOVE.L #ClearText,-(sp)         * Place address of clear text display area command sequence on stack.
            BRA SendLine                    * See note (****)


* SUBROUTINE ClrPartLn. This routine clears the line, which the cursor is on, from the start to the cursor position.

ClrPartLn:  MOVE.L #ClrPrtLine,-(sp)        * Place address of clear start-to-cursor command sequence on stack.
            BRA SendLine                    * See note (****)

 
* SUBROUTINE InsertLine. This routine inserts a blank line on the text screen at the current cursor line position. Cursor is moved
*                        to the start of the new blank line.

InsertLine: MOVE.L #PutBlnkLn,-(sp)         * Place address of put-a-blank-line-on-the-screen command sequence on stack.
            BRA SendLine                    * See note (****)

 
* SUBROUTINE RemoveLine. This routine removes a line of text from the screen (effectively deleteing a line) at the current cursor
*                        line position. Cursor is moved to the start of the line moved up from below.

RemoveLine: MOVE.L #TakeTxtLn,-(sp)         * Place address of delete-a-line-on-the-screen command sequence on stack.
            BRA SendLine                    * See note (****)
 

* SUBROUTINE OnOverflow. This routine turns on the automatic edge-screen overflow.

OnOverflow: MOVE.L #OverFlowOn,-(sp)        * Address of overflow on command sequence.
            BRA SendLine                    * See note (****)

 
* SUBROUTINE OffOvrflow. This routine turns off the automatic edge-screen overflow.

OffOvrflow: MOVE.L #OvrFlowOff,-(sp)        * Address of overflow off command sequence.
            BRA SendLine                    * See note (****)
 

* SUBROUTINE MoveCurs. This moves the cursor to the position denoted by Line & Column in the buffer starting at MvCursor.
*                      NOTE! To avoid wasteful repetition, several routines jump to SendLine. Mostly those above.

MoveCurs:   ADDI.B #32,Line                 * Offset of 32 necessary for line and column position.
            ADDI.B #32,Column
            MOVE.L #MvCursor,-(sp)          * Address of move cursor command string onto stack.
SendLine:   MOVE.W #PrntLine,-(sp)          * Place code for GEMDOS Print Line (equals 9) onto stack
            TRAP #GemDos                    * Execute the routine.
            ADDQ.W #6,sp                    * CS and return...
            RTS


* SUBROUTINE Convert. This converts the value contained in Value to the equivalent decimal ASCII string. The  finished string
*                     always starts at NumStrng. The number string will usually begin with a one or two zeros; if these are not
*                     wanted, the string start is stored in NumSrc (D6 is used as a flag and counter to find the first non-zero
*                     digit).

Convert:    MOVEM.L D3-D6/A3,-(sp)          * SRTBU
            MOVE.L Value,D3                 * Transfer value to be converted.
            LEA NumStrng,A3                 * Set pointer to string buffer.
            CLR.L D5
            MOVE.W #10000,D5                * Initialise number of division factor.
            MOVE.B #-1,D6                   * Initialise flag and counter.
GetMore:    MOVE.L D3,D4                    * Take copy of value.
            DIVU.W D5,D4                    * Find next digit (the contents of D4[0..7])...
            MOVE.B D4,(A3)                  * Transfer value of digit.
            BEQ ZeroDig                     * Branch if a zero digit.
            TST.B D6                        * Is this the first non-zero digit?
            BPL FlgAlrdySt                  * Branch if yes, the flag is already set.
            ANDI.L #$FF,D6                  * Otherwise, set pointer to first digit and set flag...
            NEG.B D6
            SUBQ.B #1,D6
            ADD.L #NumStrng,D6              * (pointer ready)
            MOVE.L D6,NumSrc
            CLR.B D6                        * Flag set.
            BRA FlgAlrdySt
ZeroDig:    TST.B D6                        * Is this zero necessary?
            BPL FlgAlrdySt                  * Branch if yes.
            SUBQ.B #1,D6                    * Otherwise, decrease counter.
            MOVE.B #32,(A3)+                * Set character to a space and skip the conversion to a digit...
            BRA SkipConvrt
FlgAlrdySt: ADDI.B #48,(A3)+                * Convert value to a character.
SkipConvrt: MULU.W D5,D4                    * Subtract value obtained from D3 to get value for next loop execution...
            SUB.W D4,D3
            DIVU.W #10,D5                   * Decrease division factor.
            TST.W D5                        * Finished yet?
            BNE GetMore                     * Branch if no.
            TST.B D6                        * Was a non-zero digit found?
            BPL NonZeroFnd                  * Branch if yes.
            MOVE.L #NumStrng+4,NumSrc       * Otherwise, set pointer to last zero digit.
NonZeroFnd: CLR.B (A3)                      * Set a zero byte to terminate string.
            MOVE.B #5,NumDigits             * Number is always 5 digits long.
            MOVEM.L (sp)+,D3-D6/A3          * RRU
            RTS


* SUBROUTINE GetValue. This routine converts an ASCII integer string into the equivalent integer value.
*                      NumSrc must point to the start of the ASCII string.
*                      NumDigits must contain the number of characters in the number string.
*                      The result of the routine is stored in Value.

GetValue:   MOVEM.L D5-D7/A3-A4,-(sp)       * SRTBU
            MOVEA.L NumSrc,A3               * Set pointer to start of string.
            CLR.L D4                        * Calculate address of last character plus one (for pre-decrement addressing)...
            MOVE.B NumDigits,D4
            ADD.L A3,D4
            MOVEA.L D4,A4                   * A4 now points to last characetr plus one.
            MOVEQ #1,D5                     * Initialise multiplying factor.
            CLR.L D7                        * D7 will contain the final result.
            MOVE.B #No,InvalidChr           * Assume all will go well.
GetValLoop: CMPA.L A4,A3                    * Any more characters?
            BEQ GetValEnd                   * Branch if no.
            CLR.L D6                        * D6 is used as a temporary calculation buffer.
            MOVE.B -(A4),D6                 * Get next character.
            CMPI.B #' ',D6                  * Is the character a space?
            BNE OkSoFar1                    * Banch if no.
            MOVE.B #'0',D6                  * Otherwise, set so that a space is regarded as a zero.
OkSoFar1:   SUBI.B #48,D6                   * Convert to a number (D6 should then contain a number from 0 to 9 if all is o.k.).
            BPL OkSoFar2                    * Branch if the number is positive (o.k. so far).
BadDigit:   CLR.L D7                        * Otherwise, return a zero result and set flag...
            MOVE.B #Yes,InvalidChr
            BRA GetValEnd
OkSoFar2:   CMPI.B #9,D6                    * Is the number greater than 9?
            BGT BadDigit                    * Branch if yes, the digit is not valid.
            MULU.W D5,D6                    * Otherwise, get value for next decimal column in D6.
            MULU.W #10,D5                   * Increase multiplying factor for next column (if there is one).
            ADD.L D6,D7                     * Update final result.
            BRA GetValLoop                  * and again!
GetValEnd:  MOVE.L D7,Value                 * Save result.
            MOVEM.L (sp)+,D5-D7/A3-A4       * RRU
            RTS


* SUBROUTINE PrintNum. This routine displays the number string stored at the address NumStrng at the current cursor position.
*                      The fieldwidth in which the number is to be displayed (right justified) must be in FieldWidth.

PrintNum:   MOVE.L #NumStrng,SrcPtr
            MOVEQ #5,D0                     * Skip any unwanted zeros in string to give correct field width...
            SUB.B FieldWidth,D0
            ADD.L D0,SrcPtr
            JSR PrntStr
            RTS

* Note that the EQUates at the top of the program are not included in the symbol table produced in the final assembled program (a
* fact discovered too late). In the next version of Sorcerer, they will be changed to declared constants in the first DATA section
* below to allow easier tracing and debugging.

            SECTION DATA
NoErrMsg:   DC.B 'No Error!',0
ClrScrn:    DC.B 27,'E',0                   * Clear screen and Home cursor command sequence.
ClrRemLine: DC.B 27,'K',0                   * Clear rest of line from & including current cursor position.
ClrAllLine: DC.B 27,'l',0                   * Clear entire line where cursor is.
ClrPrtLine: DC.B 27,'o',0                   * Clear line where cursor is from start to cursor position.
PutBlnkLn:  DC.B 27,'L',0                   * Insert a blank line onto the text screen.
TakeTxtLn:  DC.B 27,'M',0                   * Exact opposite of PutBlnkLn.
OverFlowOn: DC.B 27,'v',0                   * Character printing overflow on sequence.
OvrFlowOff: DC.B 27,'w',0                   * Character printing overflow off sequence.
RevrsOn:    DC.B 27,'p',0                   * Reverse mode on sequence (see ReverseOn).
RevrsOff:   DC.B 27,'q',0                   * Reverse mode off sequence (see ReverseOff).
CursOn:     DC.B 27,'e',0                   * Cursor on command sequence.
CursOff:    DC.B 27,'f',0                   * Cursor off command sequence.
            SECTION BSS
Value:      DS.L 1                          * Used by GetVal and Convert (see routines for details).
FieldWidth: DS.B 1                          * Used by Convert as a field width for the result (see routine for details).
NumStrng:   DS.B 10                         * Buffer for outputting number string (see Convert).
NumSrc:     DS.L 1                          * Pointer to the number string buffer (to first digit if fieldwidth irrelevant).
NumDigits:  DS.B 1                          * Number of digits in the string stored at NumStrng. Not always used.
MvCursor:   DS.B 2                          * Buffer for moving cursor around the screen (see Init routine for initialisation).
Line:       DS.B 1
Column:     DS.B 2                          * Need extra byte for termination byte (zero).
YCo_ord:    DS.B 1                          * Parameter for certain functions.
CursorXPos: DS.B 1                          * The current position of the cursor is stored here.
CursorYPos: DS.B 1
OldCrsXPos: DS.B 1                          * These three variables are used to prevent unnecessary updation of 'Col:..',
OldNoLines: DS.W 1                          * 'Free:....',
OldLinePos: DS.W 1                          * and 'Line:....' respectively.
RemNewXPos: DS.B 1                          * This flag is set when the cursor is moved by a non-scrolling action.
InvalidChr: DS.B 1                          * This flag is set if an invalid character is used when entering a number.
CursAtEnd:  DS.B 1                          * This flag is set by some routines if the cursor is at the end of the line.
SkipUndoSv: DS.B 1                          * This flag is set if the operation to copy to the Undo buffer is to be ignored.
SavedLine:  DS.W 1                          * This two variables are the position marking storage variables...
SavedXPos:  DS.B 1
ActionCode: DS.B 1                          * Used by EnterLine as a code denoting what action is to be taken, if any.
NewCrsrXCo: DS.B 1                          * Used for cursor moving the cursor (see CrsrMvAc)...
NewCrsrYCo: DS.B 1
Position:   DS.B 1                          * Parameter for certain routines (such as NxtWrdLeft, et.c.).
SaveSSP:    DS.L 1                          * Supervisor stack pointer is saved here when called for (see SetConterm for example).
MaxLineLen: DS.B 1                          * The maximum length of a text line allowed.
MaxCrsrXCo: DS.B 1                          * The maximum x co-ordinate that the cursor is allowed to be (usually = MaxLineLen-1).
ScrollXPos: DS.B 1                          * Used, in conjunction with RemNewXPos, to preserve the cursor x co. during scrolling.
LineLen:    DS.B 1                          * Paramters for certain routines (see CopyData for example)...
SrcPtr:     DS.L 1
DstPtr:     DS.L 1
NumBytes:   DS.B 1
SorcPtr:    DS.L 1
DestPtr:    DS.L 1
MaxAddress: DS.L 1                          * This usually equals TextBuff + MaxLineLen and is used to prevent overflow in memory.
NumChars:   DS.B 1                          * Paramter for certain routines.
TextBuff:   DS.L 1                          * This always points to the start of the text line in memory that the cursor is on.
TempBuff:   DS.B 81                         * The temporary buffer used by EnterLine for Clr/Home and Undo.
TempBuffSt: DS.B 1                          * Set to Yes when TempBuff contains anything, No otherwise.
TmpBuffLen: DS.B 1                          * Number of characters in TempBuff.
SavedTBS:   DS.B 1                          * These three are where the ClrHomeAc variables are saved to when necessary...
SavedTBL:   DS.B 1
SavedTCX:   DS.B 1
TmpCrsrXCo: DS.B 1                          * Used by EnterLine to preserve cursor x co-ordinate during Clr/Home & Undo operations.
WordStore:  DS.B 81                         * Word manipulation store.
WrdStrLen:  DS.B 1                          * Number of characters in word store.
LineWork:   DS.B 81                         * Used by various routines (e.g. see CenterLine).
UndoBuffer: DS.B 80                         * Buffer for main editor's Undo facility.
UndoBufLen: DS.B 1                          * Set to the number of characters stored in UndoBuffer.
DelLnBuf:   DS.B 80                         * If a line is deleted with Control Y, it is remebered here.
DLBfLength: DS.B 1                          * Set to the number of characters stored in DelLnBuf.
LineChange: DS.B 1                          * Set to Yes when the cursor moves to a different line from the current one.
BuffStY:    DS.B 1                          * Used by EnterLine to denote where on the screen the single editor is to operate.
BuffLength: DS.B 1                          * Used to denote length of the buffer in question (usually the current text line).
LineLength: DS.B 1                          * These two variables are used in GetLineLen...
LineStart:  DS.L 1
SourcePtr:  DS.L 1                          * Used by EnterLine to point to the old text line (if any).
NextChar:   DS.B 1                          * Parameters for ScanKeybrd...
ScanCode:   DS.B 1
KbshiftLoc: DS.L 1
KeybrdStat: DS.B 1
UseOld:     DS.B 1                          * Used by EnterLine.
IgnoreOld:  DS.B 1                          * Used by load/save routines to cancel the effects of UseOld flag (if necessary).
TxtBlkVars:                                 * Parameters for MwsOutside...
TxtBlkMax:  DS.B 1
TxtBlkRyte: DS.B 1
TxtBlkMin:  DS.B 1
TxtBlkLeft: DS.B 1
TxtBlkSpec: DS.L 1                          * Used by certain routines to pass all four TxtBlkVars parameters at once.
QuitEnter:  DS.B 1                          * Set to yes if the user cancels the EnterLine routine when called.
InsrtState: DS.B 1                          * Text entering mode for single line text entering (Yes for Insert, No for Replace).
EdInsrtSt:  DS.B 1                          * Text entering mode for main editor.
UseMessage: DS.B 1                          * Set to Yes if current state of InsrtState is to be shown by a message.
MessageXCo: DS.B 1                          * If UseMessage is Yes, the co-cordinates of the message are placed here...
MessageYCo: DS.B 1
MainEdMXCo: DS.B 1                          * Co-ordinates for message for main editor.
MainEdMYCo: DS.B 1
SrcChar:    DS.B 1                          * Parameter for PrintChar.
            SECTION DATA
BlankStrng: DC.B '                                                                                ',0
OffMessage: DC.B 'Off',0                    * Text strings used all over the place!...
OnMessage:  DC.B 'On ',0
ReplcMess:  DC.B 'Replace',0
InsertMess: DC.B 'Insert ',0
BlankMess:  DC.B '       ',0
AYSInfMsg1: DC.B 27,'Y',44,38,'Loading a new text file will delete the current text file in memory.'
            DC.B 27,'Y',46,42,'Are you sure you want to do this?   (Click on box/Press key)',0
GetPFNMess: DC.B 27,'Y',44,32,'Enter path/file name:- (<drive>:\<directory>\<filename>)  | Typing mode:',0
ErrMessage: DC.B 27,'Y',44,32,27,'l','Error!  ',0
SavingFile: DC.B 27,'Y',43,59,'Saving text file to disk:-',0
LoadngFile: DC.B 27,'Y',43,57,'Loading text file from disk:-',0
FlExstsMsg: DC.B 27,'Y',44,35,27,'l','That file already exists! Replace existing file?  (Click on box/Press key)',0
YesBox:     DC.B 27,'p','Yes',27,'q',0
NoBox:      DC.B 27,'p','No ',27,'q',0
BlankYNBox: DC.B '   ',0
            SECTION BSS
SvOrLdFlag: DS.L 1                          * Parametr for GetPFName.
YNCoords:   DS.L 1                          * Parameters for GetYorNAns...
YNAnswer:   DS.B 1
YesBlkSpec: DS.L 1
NoBlkSpec:  DS.L 1
ReplcFlag:  DS.B 1                          * Parameter for SaveFileAs.
            SECTION DATA
CantLoadEr: DC.B 27,'Y',44,32,27,'l',27,'Y',44,49,'Error! Unable to load the specified disk file!'
            DC.B 27,'Y',46,32,27,'l',27,'Y',46,48,'The file must be a Read/Write or Read Only file.',0
CantReplc:  DC.B 27,'Y',44,32,27,'l',27,'B',27,'Y',45,32,27,'l','Error! Can''t replace file! ',0
NotRWFile:  DC.B 'The specified disk file is not one I can write to.',0
ReadOnlyFl: DC.B 'The specified disk file has been ''Write Protected''.',0
CrgRtnLnFd: DC.B $0D,$0A                    * Used during save process (sent after each text line is sent).
            SECTION BSS
DskTrnsAdd: DS.L 1                          * Disk Transfer Address placed here.
ErrorCode:  DS.B 1                          * If an error occurs (e.g. see SaveFileAs), the error number is placed here.
            SECTION DATA
NumErrMsgs: DC.L 36                         * Number of error messages below (see Init for use of).
ERROR:      DC.B '(General error)',0        * These are the error message used by Tos. Note that the last seven are error codes
EDRVNR:     DC.B '(Drive not ready)',0      * that I have created for my own use, based on this program's needs. e.g. The error
EUNCMD:     DC.B '(Unknown command)',0      * EUNXEOF (code -121) was created since I decided that, as the size of a file being
E_CRC:      DC.B '(CRC Error)',0            * loaded is obtained when first opened, if an end-of-file code (26 or 255) is
EBADRQ:     DC.B '(Bad Request)',0          * encountered when some of the file still remains to be decoded (see DecodeBlok) then
E_SEEK:     DC.B '(Seek error)',0           * something is wrong (i.e. if a file does contain an eof code, it ought to be the last
EMEDIA:     DC.B '(Unknown media)',0        * byte in the file, not somewhere in the middle).
ESECNF:     DC.B '(Sector not found)',0
EPAPER:     DC.B '(Out of paper)',0
EWRITF:     DC.B '(Write fault)',0
EREADF:     DC.B '(Read fault)',0
EWRPRO:     DC.B '(Write on protected media)',0
E_CHNG:     DC.B '(Media change detected)',0
EUNDEV:     DC.B '(Unknown device)',0
EBADSF:     DC.B '(Bad sectors on format)',0
EOTHER:     DC.B '(Insert other disk)',0
EINVFN:     DC.B '(Invalid function number)',0
EFILNF:     DC.B '(File not found)',0
EPTHNF:     DC.B '(Path not found)',0
ENHNDL:     DC.B '(Handle pool exhausted)',0
EACCDN:     DC.B '(Access denied)',0
EIHNDL:     DC.B '(Invalid handle.',0
ENSMEM:     DC.B '(Insufficient memory)',0
EIMBA:      DC.B '(Invalid memory block address)',0
EDRIVE:     DC.B '(Invalid drive specification)',0
ENMFIL:     DC.B '(No more files)',0
ERANGE:     DC.B '(Range error)',0
EINTRN:     DC.B '(GEMDOS Internal error)',0
EPLFMT:     DC.B '(Invalid executable file format)',0
EGSBF:      DC.B '(Memory block growth failure)',0
ECHAR:      DC.B '(File contains illegal characters)',0
EUNXEOF:    DC.B '(Unexpected end of file encountered)',0
ETFMEND:    DC.B 0                          * This error is used within DecodeBlok. It isn't really an error, just a condition.
EPATH:      DC.B 'Path description invalid/does not exist or last ''\'' symbol missing.',0
EPTHFLDIR:  DC.B 'Path/Directory/File is invalid, does not exist, or is missing.',0
EDRIVESPEC: DC.B 'Invalid drive specification. Must be <drv>:\[<path>\]<file name>',0
EDRIVEIDNT: DC.B 'Drive identifier invalid or drive not accessible.',0
ErrAddrLst: DC.L ERROR,EDRVNR,EUNCMD,E_CRC,EBADRQ,E_SEEK,EMEDIA,ESECNF,EPAPER,EWRITF,EREADF,EWRPRO
            DC.L E_CHNG,EUNDEV,EBADSF,EOTHER,EINVFN,EFILNF,EPTHNF,ENHNDL,EACCDN,EIHNDL,ENSMEM,EIMBA
            DC.L EDRIVE,ENMFIL,ERANGE,EINTRN,EPLFMT,EGSBF,ECHAR,EUNXEOF,ETFMEND,EPATH,EPTHFLDIR,EDRIVESPEC,EDRIVEIDNT
ErrDstList: DC.B 1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,32,33,34,35,36,37,39,40,47,49,64,65,66,67,121,122,123,124,125,126,127
            SECTION BSS
ErrCodeLst: DS.W 128                        * Error string vector table. See TosError for use of.
GotoLineBf: DS.B 6                          * The number string entered for the GotoLine function is stored here.
            SECTION DATA
QuitMessg:  DC.B 27,'Y',44,62,'Quit program.',0
QuitTitle:  DC.B 27,'Y',33,32,27,'l',27,'Y',33,61,'Quiting the program...',0
ClrFileMsg: DC.B 27,'Y',44,57,'Clear entire text file.',0
ClrFlTitle: DC.B 27,'Y',33,32,27,'l',27,'Y',33,57,'Clearing the entire text file.',0
NewFileMsg: DC.B 27,'Y',44,62,'New text file.',0
NewFlTitle: DC.B 27,'Y',33,32,27,'l',27,'Y',33,43,'Clearing the text file and file name (i.e. new text file).',0
AYSMessage: DC.B 27,'Y',46,52,'Are you sure you want to do this?',0
BasicWndw:  DC.B 27,'Y',43,32,'--------------------------------------------------------------------------------'
            DC.B 27,'Y',47,32,'--------------------------------------------------------------------------------',0
GotoLnMsg1: DC.B 27,'Y',44,33,'            GOTO LINE!  Please enter line number! (Range 1 to ',0
GotoLnMsg2: DC.B '):-',0
ClearWindw: DC.B 27,'Y',42,32,27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B'
            DC.B 27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',0
WindowBox:  DC.B 27,'Y',42,32,'--------------------------------------------------------------------------------'
            DC.B 27,'Y',50,32,'--------------------------------------------------------------------------------',0
ClrTosBox:  DC.B 27,'Y',43,32,27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',0
TosErrBox:  DC.B 27,'Y',43,34,'----------------------------------------------------------------------------'
            DC.B 27,'Y',44,33,'| Tos error      whilst'
            DC.B 27,'Y',44,110,'|',27,'Y',45,110,'|',27,'Y',46,110,'|',27,'Y',45,33,'|',27,'Y',46,33
            DC.B '| What now?   (Click on a box/Press required key)'
            DC.B 27,'Y',47,34,'----------------------------------------------------------------------------'
            DC.B 27,'Y',44,100,27,'p','Cancel.',27,'Y',46,100,'Retry. ',27,'q',27,'Y',45,35,0
TEAlterMsg: DC.B 27,'Y',44,86,27,'p','  Alter  ',27,'Y',45,86,'path/file',27,'Y',46,86,'  name.  ',27,'q',0
TECodeMsg:  DC.B 27,'Y',44,45,'-',0
TESeekMsg:  DC.B 'seeking',0
TECrtMsg:   DC.B 'creating',0
TEOpenMsg:  DC.B 'opening',0
TELoadMsg:  DC.B 'loading',0
TESaveMsg:  DC.B 'saving',0
TECloseMsg: DC.B 'closing',0
TEDecodMsg: DC.B 'decoding',0
FlMsg:      DC.B ' file.',0
FlOpMdTabl: DC.L TESeekMsg,TECrtMsg,TEOpenMsg,TELoadMsg,TESaveMsg,TECloseMsg,TEDecodMsg
HelpPages:  DC.B 12                         * Number of pages of help information (each page is one big string).
HelpList:   DC.L HelpPage1,HelpPage2,HelpPage3,HelpPage4,HelpPage4a,HelpPage5,HelpPage6
            DC.L HelpPage7,HelpPage8,HelpPage9,HelpPage10,HelpPage11
HelpPage1:  DC.B 27,'Y',36,55,'- Alternate Combination Controls -',10,10,13
            DC.B 'Alt. + ...            Function Description.',10,10,13
            DC.B '        L             Load a file from disk.',10,13
            DC.B '        S             Save a file to disk as...',10,13
            DC.B '        S + Shift     Save file to disk using current path and file name.',10,13
            DC.B '        G             Goto line... (same as clicking with mouse on ''Line:....'').'
            DC.B '        T             Move cursor to top of text file.',10,13
            DC.B '        B             Move cursor to bottom of text file.',10,13
            DC.B '        M             Center text on current line in middle of screen - if the',10,13
            DC.B '                      line contains an odd number of characters, the screen',10,13
            DC.B '                      flashes once (press Undo to return line to original',10,13
            DC.B '                      state). If no flash, the cursor is moved down one line.',10,13
            DC.B '       ESC            Invert screen.',10,13
            DC.B '        C             Clear text file (press Shift as well for new text file).',10,13
            DC.B '        Q             Quit program (''Are you sure?'' prompt given for C & Q).',0
HelpPage2:  DC.B 27,'Y',36,61,'- Basic Key Controls -',10,10,13
            DC.B '        Undo           Every time the cursor is moved, in any way, to a',10,13
            DC.B '                       different line, a copy of the line is stored. Any',10,13
            DC.B '                       changes then made to that text line can be undone (whilst'
            DC.B '                       STILL ON THE LINE) by pressing Undo, which replaces the',10,13
            DC.B '                       text line on screen with the stored copy. Any function',10,13
            DC.B '                       described on these pages which is ''undoable'' is marked',10,13
            DC.B '                       with the symbols (*U*).',10,13
            DC.B 'Any valid character:   Character placed onto current line (*U*); see Overflow,',10,13
            DC.B '                       Insert & Word Wrap info (on next few pages) for details.',10,13
            DC.B '       Return          Cut all characters from cursor position onto a new line.',10,13
            DC.B '       Delete          Delete character at cursor position; text absorbed from',10,13
            DC.B '                       right - cursor does not move (*U*).',10,13
            DC.B '    Shift + Delete     If cursor not at end of line, same as Delete - else drag',10,13
            DC.B '                       text from line below onto end of current line IF THERE IS'
            DC.B '                       SUFFICIENT ROOM. The cursor does not move.',0
HelpPage3:  DC.B 27,'Y',36,61,'- Basic Key Controls -',10,10,13
            DC.B '    Shift + Undo       Sets the Undo buffer to the state of the current line,',10,13
            DC.B '     Backspace         If at start of line, does nothing - else delete the',10,13
            DC.B '                       character to the left of the cursor position and move the'
            DC.B '                       cursor left one position (*U*),',10,13
            DC.B ' Shift + Backspace     If not at start of line, same as Backspace - else drag',10,13
            DC.B '                       text on current line onto end of line above, if there is',10,13
            DC.B '                       room (does nothing if at start of text).',10,13
            DC.B '    Control + K        Clear contents of current line (*U*),',10,13
            DC.B '    Control + Y        Line destroy: totally remove the current line. Undoable',10,13
            DC.B '                       with Shift + Control + Y. If last line deleted, must make'
            DC.B '                       new blank last line for Shift + Control + Y to work o.k.,'
            DC.B '      Clr/Home         Jump cursor to top left of screen,',10,13
            DC.B '  Shift + Clr/Home     Jump cursor to bottom left of screen (as far as possible)'
            DC.B '        ESC            Cancel function (Goto line, Load, Save, Quit & Help),',10,13
            DC.B '        Help           Gives the information you are seeing now.',0
HelpPage4:  DC.B 27,'Y',36,61,'- Basic Key Controls -',10,10,13
            DC.B '       Insert          Toggle the text entering mode between Insert and Replace',10,13
            DC.B '                       (shown by ''MD:.......'' at the top of the screen).',10,13
            DC.B '                       Insert:- the character is inserted at the current cursor',10,13
            DC.B '                       position after the rest of the characters from the cursor'
            DC.B '                       onwards have been shunted right one position;',10,13
            DC.B '                       Replace:- typing overwrites characters already on the',10,13
            DC.B '                       line, but overflow acts as normal. N.B.: Using the Insert'
            DC.B '                       key has the same effect as clicking on ''MD:.......'',',10,13
            DC.B '    Control + O        Toggle Overflow mode (shown by ''OFW:...'' at the top of',10,13
            DC.B '                       the screen). If on: if cursor is at column 80, continued',10,13
            DC.B '                       typing will overflow onto a new line. If off: characters',10,13
            DC.B '                       can still be typed irrespective of the current column,',10,13
            DC.B '                       but a new line will not be taken when at column 80 - the',10,13
            DC.B '                       old character will simply be overwritten. N.B.: Pressing',10,13
            DC.B '                       Control + O is equivalent to clicking on ''OFW:.......'',',0
HelpPage4a: DC.B 27,'Y',36,54,'- Word Wrap, & Word Processor mode -',10,10,13
            DC.B '     When Word Processor mode is off (shown at top of screen by ''WP:...''),',10,13
            DC.B 'typing at the keyboard when Overflow is On and the cursor is at column 80 will',10,13
            DC.B 'simply place the next character typed, no matter what it is, onto a new line.',10,13
            DC.B '     If, however, under the same conditions when Word Processor mode is On, then'
            DC.B 'what happens when typing overflows onto a new line depends on the kind of',10,13
            DC.B 'character typed next at column 1 of the new line...',10,13
            DC.B '     If the character is a space, it is ignored, but only once. Any further',10,13
            DC.B 'spaces will be shown. If the character is not a space, it is assumed that the',10,13
            DC.B 'word being typed is split between the two lines and the first part of the word',10,13
            DC.B 'at the end of the previous line is moved to the start of the new line (this is',10,13
            DC.B 'known as Word Wrap).',10,13
            DC.B '     The upshot of this is that when Word Processor mode is On, one need not',10,13
            DC.B 'worry about words being split at the end of a line when typing.',10,13
            DC.B '     Word Processor mode can be toggled On and Off by pressing Control + W, or',10,13
            DC.B 'by clicking on ''WP:...'',',0
HelpPage5:  DC.B 27,'Y',36,49,'- Cursor movement (by using the cursor keys) -',10,10,13
            DC.B '                  Left Arrow           Cursor left one positon,',10,13
            DC.B '              Shift + Left Arrow       Cursor left one word,',10,13
            DC.B '             Control + Left Arrow      Jump cursor to start of line,',10,13
            DC.B '                 Right Arrow           Cursor right one positon,',10,13
            DC.B '              Shift + Right Arrow      Cursor right one word,',10,13
            DC.B '             Control + Right Arrow     Jump cursor to end of line,',10,13
            DC.B '                   Up Arrow            Cursor up one position,',10,13
            DC.B '               Shift + Up Arrow        Scroll text down half a page,',10,13
            DC.B '              Control + Up Arrow       Jump text down one page,',10,13
            DC.B '          Shift + Control + Up Arrow   Scroll text down one line,',10,13
            DC.B '                  Down Arrow           Cursor down one position,',10,13
            DC.B '              Shift + Down Arrow       Scroll text up half a page',10,13
            DC.B '             Control + Down Arrow      Jump text up one page,',10,13
            DC.B '         Shift + Control + Down Arrow  Scroll text up one line,',0
HelpPage6:  DC.B 27,'Y',36,52,'- Cursor movement (by using the mouse) -',10,10,13
            DC.B '     Click anywhere on the text, with the left button, to position the cursor',10,13
            DC.B '(please see information concerning right button use), OR click on the inverted',10,13
            DC.B 'arrow symbols either side of the word ''Cursor'' - these symbols, when clicked',10,13
            DC.B 'on, act just as if they were keys (including all Shift & Control combinations).',10,10,13
            DC.B '     Other mouse controls not mentioned on previous pages:-',10,10,13
            DC.B '     Clicking on the inverted up or down arrow symbols either side of the word',10,13
            DC.B '''Text'' scrolls the text up or down one line appropriately.',10,10,13
            DC.B '     Clicking on the inverted up or down arrow symbols either side of the word',10,13
            DC.B '''Page'' jumps the text up or down one page appropriately. If either Shift key is',10,13
            DC.B 'pressed at the same time, the text scrolls only half a page up or down.',0
HelpPage7:  DC.B 27,'Y',36,53,'- Mouse Controlled Word Manipulation -',10,10,13
            DC.B '     To remove a word from a line, click on it with the mouse right button. The',10,13
            DC.B 'word is not actually deleted, but is stored in a temporary buffer (NOT the Undo',10,13
            DC.B 'memory buffer). Thus, the word can be inserted anywhere else in the text by',10,13
            DC.B 'keeping either Shift key down and clicking (right button) on the appropriate',10,13
            DC.B 'place where the word is to go. The buffer only holds one word at a time.',10,10,13
            DC.B '     Repeated word removals keep only the last word removed in the buffer (Note:'
            DC.B 'once a word is re-inserted, the buffer is cleared - after this, Shift + Right',10,13
            DC.B 'button click only moves the cursor, just as left button would. Note 2: Pressing',10,13
            DC.B 'the right button on an empty line merely moves the cursor to that line).',10,13
            DC.B '     If the right button is clicked over a single space between words, the word',10,13
            DC.B 'to the right of the space is removed. If, however, the right button is pressed',10,13
            DC.B 'over a block of spaces, all the spaces except one are deleted.',10,13
            DC.B 'This facility is only Undoable if the words are deleted/moved onto the same',10,13
            DC.B 'line each time (i.e. if cursor moves to a different line, Undo won''t work).',0
HelpPage8:  DC.B 27,'Y',36,62,'- Position Marking -',10,10,13
            DC.B '     The current position of the cursor can be ''remembered'' by pressing either',10,13
            DC.B 'Shift key and clicking on the word ''Cursor'' at the top of the screen, which will'
            DC.B 'flash once when this is done (Control + R has the same effect).',10,13
            DC.B '     At any time, the cursor can be jumped back to this remembered popsition (if'
            DC.B 'there is one) by pressing Control and clicking on the word ''Cursor''. Nothing',10,13
            DC.B 'will happen if no position has been ''remembered'', otherwise, the word ''Cursor''',10,13
            DC.B 'will flash once and the cursor will be jumped to the remembered position (this',10,13
            DC.B 'is the same as Control + J).',10,13
            DC.B '     To prevent accidental cursor jumping, the Remember Position memory can be',10,13
            DC.B 'cleared by just clicking on the word ''Cursor'' on its own; the word ''Cursor''',10,13
            DC.B 'will flash once if this is possible and is done (same as Control + E) .',10,10,13
            DC.B 27,'Y',49,62,'Final mouse notes...',10,10,13
            DC.B '   (i)  Click on Desk for information about this progam,',10,13
            DC.B '   (ii) See notes on next page concerning single line text entering.',0
HelpPage9:  DC.B 27,'Y',36,32,'- Single Line Text Entering (e.g.  Path/file names, Goto Line function, et.c.) -',10,13
            DC.B '      The following rules apply when entering text/numbers in these situations:-',10,13
            DC.B '(i)   None of the mouse operated controls at the top of the screen are useable,',10,13
            DC.B '(ii)  The cursor may still be positioned using the mouse in the normal way (word'
            DC.B '      deletion/insertion with right button still operational),',10,13
            DC.B '(iii) Character insertion as usual (only Insert toggles the text entering mode),'
            DC.B '(iv)  Overflow is similar to On (characters can fall of end of the line ,if the',10,13
            DC.B '      line is full and the current text entering mode is Insert),',10,13
            DC.B '(v)   The line can be cleared with Clr/Home (the line is removed to a temporary',10,13
            DC.B '      buffer, a different buffer from the normal Undo buffer).',10,13
            DC.B '(vi)  If the line has been cleared by Clr/Home, it can be brought back from the',10,13
            DC.B '      buffer with Undo (temporary buffer is then cleared).',10,13
            DC.B '(vii) Pressing Alternate + Undo will restore the line to the state it originally'
            DC.B '      was in when the function being used was first entered (this does not',10,13
            DC.B '      affect anything saved with Clr/Home).',0
HelpPage10: DC.B 27,'Y',36,56,'- General Notes On Program Use -',10,10,13
            DC.B '     When confonted with a mouse prompt (e.g.: an ''O.K.'' box), it is always the',10,13
            DC.B 'case that a key press can be used instead of the mouse. These are:-',10,10,13
            DC.B '  Prompt     Key that can be used.            Prompt     Key that can be used.',10,10,13
            DC.B '   ',27,'pO.K.',27,'q             Return                   ',27,'p  Alter  ',27,'q',10,13
            DC.B 27,'Y',44,77,27,'pPath/File',27,'q            A',10,13
            DC.B '  ',27,'pMore...',27,'q           Return                   ',27,'p  name.  ',27,'q',10,10,13
            DC.B '   ',27,'pYes',27,'q                Y                       ',27,'pRetry. ',27,'q             R',10,10,13
            DC.B '   ',27,'pNo ',27,'q                N                       ',27,'pCancel.',27,'q             C',10,10,13
            DC.B '  ',27,'pHmm....',27,'q           Return',0
HelpPage11: DC.B 27,'Y',44,32,'   End of Program Help Information. More Help pages will be added in future',10,13
            DC.B 'versions of this program. Note: If you find any bugs in the program, please',10,13
            DC.B 'contact the author of the program who will be most grateful for the information.',0
HlpPageMsg: DC.B 27,'Y',33,32,27,'l',27,'Y',33,60,'Help information: Page ',0
NxtPageMsg: DC.B 27,'Y',54,39,'Press Esc to cancel, or click on box/press Return for next page...',0
            SECTION BSS
ScrnChange: DS.B 1                          * Used by certain routines to see if the screen display has changed.
ClrOldChrs: DS.B 1                          * This is set to Yes if ShowLine is also to clear some garbage at the end of a line.
TsErrReply: DS.B 1                          * Result paramter for TosError.
Cancelled:  DS.B 1                          * Set to Yes if EnterLine was cancelled.
CancFlag:   DS.B 1                          * Parameter used by the mouse message routines for WaitForClk.
FileOpMode: DS.B 1                          * Parameter for TosError.
CurrentDrv: DS.B 1                          * Contains the active current drive.
PathName:   DS.B 70                         * Contains current path name.
PathScan:   DS.B 1                          * Paramter for CheckPFNam.
Found:      DS.B 1                          * Used by CheckPFNam.
FileName:   DS.B 13                         * Contains current file name.
FileHandle: DS.W 1                          * Used by disk access routines.
PthFileNam: DS.B 81                         * Buffers for path file names: this is the temporary one,
OldPFName:  DS.B 81                         * and this is the current one.
FrAlcUnits: DS.L 1                          * (number of Free Allocation Units)   These are parameters for GetDskFSpc...
TotAlcUnts: DS.L 1                          * (Total number of Allocation Units)
BytsPrSctr: DS.L 1                          * (number of Bytes Per Sector)
PhysSecAlc: DS.L 1                          * (number of Physical Sectors per Allocation unit)
DskFreeSpc: DS.L 1                          * Disk Free Space = BytsPrSctr * PhysSecAlc * FrAlcUnits
ShntLnStrt: DS.W 1                          * Parameters for ShuntUp and ShuntDown...
ShuntDist:  DS.W 1
LineNumber: DS.W 1                          * Parameter for various routines.
FileStart:  DS.L 1                          * Pointer to the start of the TFM.
MaxNoLines: DS.W 1                          * Maximum number of lines allowed in text file (see InitFile).
NumLines:   DS.W 1                          * Current number of lines in text file.
CurrntLine: DS.W 1                          * Current physical line number which cursor is on.
CrntLnStrt: DS.L 1                          * Contains start address of current line in the TFM.
CrntLnLen:  DS.B 1                          * Contains line length of current line.
CurntLnYCo: DS.B 1                          * Contains screen y co-ordinate of current line.
TopLine:    DS.W 1                          * Contains relative line number of the line at the top of the text window area.
Overflow:   DS.B 1                          * Overflow state flag.
WrdProMode: DS.B 1                          * Word Processor state flag.
NewLnTaken: DS.B 1                          * Used by the routine Character for word wrap...
NwLnTknLn:  DS.W 1
FileSize:   DS.L 1                          * Used by GetDskFile.
MouseXCo:   DS.W 1                          * Current mouse co-ords stored here...
MouseYCo:   DS.W 1
OldMwsXCo:  DS.W 1                          * Co-ords of the mouse last time round stored here...
OldMwsYCo:  DS.W 1
MwsChrXPos: DS.B 1                          * Equivalent character cursor position of the mouse stored here...
MwsChrYPos: DS.B 1
RightBut:   DS.B 1                          * Flags to denote various things (see ScanKeybrd; Set to either Yes or No)...
LeftBut:    DS.B 1
ButChange:  DS.B 1
MwsChange:  DS.B 1
MwsInside:  DS.B 1
HasClicked: DS.B 1
MwsFlag:    DS.B 1                          * Flag used to denote whether a mouse action was just a cursor movement.
MwsAcFlag:  DS.B 1                          * Set to Yes when anything mouse related occurs.
OrignalRes: DS.W 1
CurrentRes: DS.W 1
NewPallete: DS.W 16
PalleteBuf: DS.W 16
NumBlocks:  DS.W 1
FinalBlkSz: DS.W 1
BlockSize:  DS.L 1
WorkSpace:  DS.L 1280                       * This number should equal the constant at WrkSpcSize DIV 4.
ScreenStrt: DS.L 1
CharTblPtr: DS.L 1

            SECTION DATA
WrkSpcSize: DC.L 5120
OkMessage:  DC.B 'O.K.',0
BlankOkMsg: DC.B '    ',0
HmmMsg:     DC.B 'Hmm...',0
BlankHmMsg: DC.B '      ',0
MoreMsg:    DC.B 'More...',0
BlankMrMsg: DC.B '       ',0
DeskInfMsg: DC.B 27,'Y',44,47,'Sorcerer Eighty Column Text Editor/Word Processor,'
            DC.B 27,'Y',45,50,'Sorcerer Software Unlimited, December 1991, DV1.2,'
            DC.B 27,'Y',46,51,'Written by Ian Mapleson in 100% Assembler.',0
DskInfTitl: DC.B 27,'Y',33,32,27,'l',27,'Y',33,61,'Program Information...',0
*                                   Screen display details...
NoneLeft:   DC.B 27,'Y',34,54,'   0',0         * This string only gets shown if the TFM is completely full (a rare event!).
ScrnDsply1: DC.B 'Desk',0
ScrnDsply2: DC.B 27,'Y',33,32,'                          Press Help for key definitions and useful information.',0
ScrnDsply3: DC.B 27,'Y',34,32,'Line:     Col:   Free:    lns   Cursor    Text   Page  MD:Insert  OFW:On  WP:On',0
UpArrwList: DC.B 31,41,48                   * These are the x co-ordinates where the inverted up/down arrows are to be shown...
DnArrwList: DC.B 38,46,53
LArrow:     DC.B 27,'Y',34,62,27,'p',0
RArrow:     DC.B 27,'Y',34,71,0
LineFill:   DC.W $FFFF                      * This is used as the fill pattern to give a solid bar on the screen. See ScrnDsplay.
CursNormal: DC.B 27,'Y',34,64,'Cursor',0    * These 2 are used for showing the activation of the position marking functions...
CursInvrtd: DC.B 27,'Y',34,64,27,'p','Cursor',27,'q',0
SaveTitle:  DC.B 27,'Y',33,32,27,'l',27,'Y',33,64,'Saving a file...',0  * These 3 are the messages that replace the Help message
LoadTitle:  DC.B 27,'Y',33,32,27,'l',27,'Y',33,63,'Loading a file:-',0  * when the function concerned is called...
GotoLnTitl: DC.B 27,'Y',33,32,27,'l',27,'Y',33,62,'Goto Line Function:-',0
*                                           * The following string clears the text window area.
ClearText:  DC.B 27,'Y',36,32,27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B'
            DC.B 27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B'
            DC.B 27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',27,'B',27,'l',0

            DC.L 1                          * Must ensure mouse data begins on an even address boundary.
Mouse:      DC.W 7                          * X hot spot for mouse sprite.
            DC.W 7                          * Y hot spot for mouse sprite.
            DC.W 1                          * Reserved, must be 1.
            DC.W 0                          * Mask colour value.
            DC.W 1                          * Data colour value.
Mask:       DC.W %0000001111000000          * The data is used to change the mouse sprite to a cross hair. Can't stand arrows; so
            DC.W %0000001111000000          * utterly boring. Cross hair much better. Somebody should tell Atari...
            DC.W %0000001111000000
            DC.W %0001101111011000
            DC.W %0001111111111000
            DC.W %0000111111110000
            DC.W %1111111111111111
            DC.W %1111111111111111
            DC.W %1111111111111111
            DC.W %1111111111111111
            DC.W %0000111111110000
            DC.W %0001111111111000
            DC.W %0001101111011000
            DC.W %0000001111000000
            DC.W %0000001111000000
            DC.W %0000001111000000

MData:      DC.W %0000000000000000
            DC.W %0000000110000000
            DC.W %0000000110000000
            DC.W %0000000110000000
            DC.W %0000100110010000
            DC.W %0000010000100000
            DC.W %0000000110000000
            DC.W %0111101001011110
            DC.W %0111101001011110
            DC.W %0000000110000000
            DC.W %0000010000100000
            DC.W %0000100110010000
            DC.W %0000000110000000
            DC.W %0000000110000000
            DC.W %0000000110000000
            DC.W %0000000000000000

            END                             * End of listing (thank grud).
    
