This is a GUI module of EuroTool program EuroView for Linux.
EUROASM CPU=X64, Unicode=No, DumpWidth=36, Debug=no
viewling PROGRAM Format=COFF, Width=64
%DROPMACRO *
INCLUDEHEAD argument.htm, viewmain.htm
INCLUDE linabi.htm, cpuext64.htm, cpuext.htm, linsfile.htm, linf64.htm, \
ansi.htm,string64.htm, memory64.htm, time.htm, status32.htm, debug.htm
[.rodata] ; Constant data.
Help:
DB " EuroView is one of the EuroTool from https://euroassembler.eu/eurotool/",10
DB " which can be use to inspect binary, object, library or executable ""InputFile"":",10
DB " ./euroview.x ""InputFile""",10,10
DB " It also accepts arguments from the command-line or from /etc/eurotool/euroview.ini:",10
DB 9,"-InputFile= ",9,9,"Input file name to be inspected/",10
DB 9,"-FileFormat= ",9,9,"Default is autodetect.",10
DB 9,"-OutputFile= ",9,9,"Temporary file; default is ""/tmp/InputFile.lst"".",10
DB 9,"-LeaveTemporary=no",9,9,"Do not erase OutputFile when EuroView terminates.",10
DB 9,"-CodeWidth= ",9,9,"Set to 16|32|64 if not autodetected.",10
DB 9,"-CodeBegin= ",9,9,"File address where to begin the disassembly..",10
DB 9,"-CodeEnd= ",9,9,"File address where to end the disassembly",10
DB 9,"-DataBegin= ",9,9,"File address where to begin the dumping as data.",10
DB 9,"-DataEnde= ",9,9,"File address where to end the dumping as data.",10
DB 10
DB " Accepted file formats:",10
%i %SETA 1
%WHILE "%FileFormats{%i}"
DB 9,"%FileFormats{%i+0}"
DB 9,"%FileFormats{%i+1}"
DB 9,"%FileFormats{%i+2}"
DB 9,"%FileFormats{%i+3}"
DB 9,"%FileFormats{%i+4}"
DB 9,"%FileFormats{%i+5}"
DB 10
%i %SETA %i+6
%ENDWHILE
DB 10," Keys:",10
DB 9,"Tab | Left button",9,9,"decrease nesting level (more detailed)",10
DB 9,"Shift Tab | Right button",9,"increase nesting level (more compact)",10
DB 9,"F",9,9,9,9,"find a text string.",10
DB 9,"N",9,9,9,9,"find the next occurence",10
DB 9,"P",9,9,9,9,"find the previous occurence",10
DB 9,"Home",9,9,9,9,"beginning of the document",10
DB 9,"End",9,9,9,9,"end of the document",10
DB 9,"PgUp | PgDn",9,9,9,"page up | down",10
DB 9,"Up | Dn | mouse wheel",9,9,"line up | down",10
DB 9,"Esc",9,9,9,9,"close window, terminate program.",10
DB 0
FormatNames:
FF %FOR %FileFormats
DB "%FF",0
%ENDFOR FF
EndFormatNames:
Spaces DB NAME_MAX * 2 * BYTE ' '
[.data] ; Variable data. TimeSpec1 DS TIMESPEC, .tv_sec=1, .tv_nsec=200_000_000 ValResize DB 'xxxxxxxxx' ; Replaced by '24;150t',0 ScrollSlider DB 0xE2,0x96,0x88,0xE2,0x96,0x88,0 ; 2 * FULL BLOCK. ScrollBar DB 0xE2,0x96,0x92,0xE2,0x96,0x92,0 ; 2 * MEDIUM SHADE.
[.bss] ; Working memory variables.
File_ini DS FILE64 ; Configuration FILE64.
FileInput DS FILE64 ; Input FILE64.
FileOutput DS FILE64 ; Output FILE64.lst in ASCII.
FileIndex DS FILE64 ; Temporary FILE64.lst.index with VIEW_INDEX records.
TtySize DS TTYSIZE
TermIO:: DS TERMIO
LstIndex: DS LST_INDEX
TermWidth DD DWORD ; Terminal width in columns.
TermHeight DD DWORD ; Number of rows in the terminal window.
DirHeight DD DWORD ; Number of rows in the directory window, without borders.
Level DD DWORD ; What level of headers to display (0..4).
LevelPrevious DD DWORD ; Used to test the change of Level.
ListPos DD DWORD ; Ordinal of the listing line displayed in the top row of the window.
ListMax DD DWORD ; Number of rows in the listing.
FoundCnt DD DWORD ; How many times was the string Find$ found in output file.
FindCursor DD DWORD ; Offset of cursor from the beginning of edit field 0,1,2....
Find$Size DD DWORD ; Size of the string to find in bytes.
FindEditLen DD DWORD ; Size of find-edit field in characters.
FindEditWidth DD DWORD ; Maximal size of visible editor field.
ALIGN 16
FindEdit DB 256* BYTE ; Searched string being edited.
Find$ DB 256* BYTE ; Searched string final version, approved by Enter in Find dialogue.
KeyBuffer DB 32 * BYTE ; Buffer for one character read from keyboard.
[.text]
Main:: PROC
StdOutput EuroView::, Version::, Eol=yes
; Try to load arguments from the configuration file /etc/eurotool/euroview.ini
.
FileAssign File_ini,=B'/etc/eurotool/euroview.ini'
FileExists? File_ini
JNC .20:
CALL ReadFile_ini
.20:MOV EAX,[ArgNr::] ; Read arguments from the command-line.
INC EAX
MOV [ArgNr::],EAX
GetArg RAX, Frame=RSP ; Returns the line in RSI,RCX with one argument, e. g. -IF="~/file.obj"
JC .40: ; If there're no more arguments.
CALL ArgParse:: ; Use ArgParse to translate it to a public symbol Arg***.
JNC .20:
StdOutput ErrorMessage::
JMP ErrorHelp:
.40:LEA RSI,[ArgInputFile::]
CMPB [RSI],0
JZ ErrorHelp:
LEA RBX,[FileInput]
FileAssign RBX,RSI
FileExists? RBX
JNC .50:
LEA RSI,[RBX+FILE64.Name]
StdOutput =B"File """,RSI,=B""" was not found.",Eol=yes
TerminateProgram 4
.50:; Input file was specified.
BufferCreate Size=64
JC ErrorAlloc:
MOV [ExportBuffer::],RAX
; Create five level buffers.
BufferCreate Size=256K
JC ErrorAlloc:
MOV [Level0Buffer::],RAX
lvl %FOR 1..4
BufferCreate Size=64K
JC ErrorAlloc:
MOV [Level%lvl{}Buffer::],RAX
%ENDFOR
LEA RAX,[FileIndexReopen:]
MOV [FileIndexReopenProc::],RAX
LEA RAX,[FileWriteOutput:]
MOV [FileWriteOutputProc::],RAX
MOVD [Level],0
MOVD [ListPos],1
CALL ViewPrologue
CALL ViewCreate:: ; External procedure in viewmain.htm
.
CALL ViewEpilogue
CALL WindowResize
CALL WindowPaint
CALL WindowProc
FileClose FileOutput, FileIndex
JSt [Status::],ArgLeaveTemporary,.90:
FileDelete FileOutput, FileIndex
.90:TerminateProgram
ENDP Main::
FileIndexReopen: PROC
LEA RBX,[FileIndex]
FileClose RBX
FileMapCreate RBX ; Close and reopen FileIndex. Returns the contents in RDI,RAX.
JC ErrorFileIndex:
RET
ENDP FileIndexReopen:
FileWriteOutput: PROC
FileWrite FileOutput,RSI,RCX
JC ErrorFileOutput:
RET
ENDP FileWriteOutput:
.Size, add .Size to .FA, clear .Level and
.Rem in this variable.
SaveViewIndex:: PROC
PUSH RCX
FileWrite FileIndex, RBX, 16
MOV EAX,[RBX+VIEW_INDEX.Size]
MOV ECX,EAX
AND EAX,0x0FFF_FFFF ; Get rid of Level bits.
MOV [RBX+VIEW_INDEX.Size],EAX ; Keep .Size unchanged for the next element.
SHR ECX,28 ; Isolate level.
JNZ .50:
ADD [RBX+VIEW_INDEX.FA],EAX ; Add previous .Size to this .FA, but keep .FA unchanged when .Level >0.
.50: XOR EAX,EAX
MOV [RBX+VIEW_INDEX.Rem],RAX ; Always clear .Rem field.
POP RCX
RET
ENDP SaveViewIndex::
WindowResize PROC
LinABI ioctl,0,TIOCGWINSZ,TtySize
MOVZXW EDX,[TtySize.ts_lines]
MOVZXW ECX,[TtySize.ts_cols]
MOV EAX,20 ; Minimal acceptable lines.
CMP EDX,EAX
JB .10:
MOV EAX,EDX
.10:LEA RDI,[ValResize]
StoD RDI
MOV AL,';'
STOSB
MOV EAX,150 ; Minimal acceptable columns.
CMP ECX,EAX
JB .13:
MOV EAX,ECX
.13:StoD RDI
MOV AX,'t'
STOSW
LinABI ioctl,0,TIOCGWINSZ,TtySize
MOVZXW EDX,[TtySize.ts_lines]
MOV [TermHeight],EDX
MOVZXW ECX,[TtySize.ts_cols]
MOV [TermWidth],ECX
RET
ENDP WindowResize
File_iniin UTF-8 and parses its each line into configuration variables Arg*** in UTF-8, too.
ReadFile_ini:: PROC
SetSt [Status::],ArgFromFile ; Tell ArgParse that arguments may not begin with / or -.
LEA RDI,[WorkMemory::]
Concat$ RDI, RDI,=B'Configuration "',File_ini.Name,=B'"'
.10:FileStreamOpen File_ini,BufSize=4K
JNC .20:
Concat$ RDI,RDI,=B'" was not found.',=B(10)
JMP .90:
.20:FileStreamReadLn File_ini
JBE .80:
MOV ECX,EAX
; The first line may begin with BOM.
MOV AX,[RSI]
CMPW AX,0xBBEF ; UTF-8 BOM?
JNE .50:
ADD RSI,3 ; Skip the BOM.
SUB ECX,3
JB .80:
JMP .50:
.30:FileStreamReadLn File_ini
JBE .80:
MOV ECX,EAX
.50:CALL ArgParse::
JNC .30:
StdOutput ErrorMessage::, Help
TerminateProgram 8
.80:FileClose File_ini
LEA RDI,[WorkMemory::]
Concat$ RDI,RDI,=B'" was accepted.',=B(10)
.90:RstSt [Status::],ArgFromFile
RET
ENDP ReadFile_ini
ArgOutputFile(if not empty) or
/tmp/ArgInputFileName.lst,
/tmp/ArgInputFileName.lst.index(if ArgOutputFile= is empty).
ViewPrologue: PROC
LEA RSI,[ArgInputFile::] ; Memory-map FileInput.
CMPB [RSI],0
JZ ErrorFileInput:
LEA RBX,[FileInput]
FileAssign RBX,RSI
FileMapOpen RBX
JC ErrorFileInput:
MOV [InputPtr::],RSI
MOV [InputSize::],RAX
LEA RDX,[RSI+RAX]
MOV [InputEnd::],RDX
BSR ECX,EAX
MOV EDX,4
ADD ECX,EDX
SHR ECX,2
CMP ECX,EDX ; Number of address digits (4..8) in listing.
JA .10:
MOV ECX,EDX
.10:MOV [AddressDigits::],ECX
MOV EDX,[ArgFileFormat::] ; Find file format.
TEST EDX
JNZ .20:
CALL AutodetectFileFormat::
MOV [FormatProcPtr::],RAX ; FormatELF32, FormatBIN etc.
MOV [ArgFileFormat::],ECX ; Ordinal format number.
.20:XOR EDI,EDI
MOV EDX,[ArgFileFormat::] ; Set by AutodetectFormat or by cmd-line argument.
LEA RDI,[FormatNames:] ; Find the format uppercase name.
MOV ECX,EndFormatNames-FormatNames
XOR EAX,EAX
.25:DEC EDX
JZ .28:
REPNE SCASB
JMP .25:
.28:MOV [FormatNamePtr::],RDI
LEA RBX,[FileOutput]
LEA RSI,[ArgOutputFile::]
CMPB [RSI],0
JZ .30:
FileAssign RBX,RSI
JC ErrorFileOutput:
JMP .40:
.30:; If ArgOutputFile is not specified (default), use /tmp/ and filename without path for temporary names.
LEA RSI,[FileInput.Name]
MOV EAX,[FileInput.NameOffs]
ADD RSI,RAX
FileAssign RBX,=B"/tmp/",RSI,=b".lst"
.40:FileCreate RBX
JC ErrorFileOutput:
LEA RSI,[RBX+FILE64.Name]
FileAssign FileIndex, RSI, =B".index"
FileCreate FileIndex
JC ErrorFileIndex
RET
ENDP ViewPrologue
ViewEpilogue:: PROC
FileClose FileOutput, FileIndex
FileMapOpen FileOutput
JC ErrorFileOutput
MOV [OutputPtr::],RSI
MOV [OutputSize::],RAX
ADD RAX,RSI
MOV [OutputEnd::],RAX
FileMapOpen FileIndex
JC ErrorFileIndex
MOV [IndexPtr::],RSI
ADD RSI,RAX
MOV [IndexEnd::],RSI
SHR EAX,4
MOV [ListMax],EAX
CALL CreateLstIndex ; Toss the index to five memory buffers for levels 0..4.
MOVD [Level],0
MOVD [ListPos],1
RET
ENDP ViewEpilogue
ListMax rows of the listing file (FileOutput).
The following example shows the listing with ListMax=22 rows,
viewed by the terminal window with TermHeight=11 rows.
Contents of the first line in terminal (title row) is fixed, it shows the program name, file name, file format,
nesting level (0..4) and how many times was a serched string found, for instance
EuroView "file.o" as ELF32 level 0. Found 0× "".
Vertical position of the displayed section of listing in the terminal window is specified by
[ListPos], which is the ordinal number of this listing row, which is displayed
in terminal row 1 (just below the title row).
Cursor keys and mouse wheel manipulate only with this variable ListPos, it is saturated to
0..ListMax.
The last column in terminal window is dedicated to scroll box. The height of its slider is
ScrollHeight=(TermHeight-1)*(TermHeight-1)/ListMax=10*10/22=4 saturated to 1..TermHeight-1=1..10.
Position of the topmost character of the slider is
ScrollPos=(TermHeight-1)*(ListPos)/(ListMax)+1=10*7/22+1=4, saturated to
1..TermHeight-1=1..10.
Listing contains ASCII characters only, no UTF-8.
WindowPaint PROC
CALL WindowTitle
StdOutput AnsiClrFg000000; Black foreground.
MOV R12,[OutputPtr::] ; Where is the text mapped in memory.
MOV EAX,[Level]
MOV EDX,[LevelPrevious]
CMP EAX,EDX
MOV [LevelPrevious],EAX
JE .40:
JB .30:
; Level is increasing from EDX to EAX. Calculate the new value of LinePos.
ADD R12,RAX ; Position of '#' in output line.
BufferRetrieve [8*RDX+Level0Buffer::] ; Let RSI,RCX point to old LST_INDEX records.
XOR EDX,EDX
MOV EDI,[ListPos]
TEST EDI
JZ .15:
.10:DEC EDI
JZ .20:
.15:LODSD ; EAX=LST_INDEX.FA
CMPB [R12+RAX],'#' ; Is the line in new level?
LODSD ; Skip LST_INDEX.Size
JNE .10:
INC EDX ; The next row.
JMP .10:
.20:TEST EDX
JNZ .25:
INC EDX
.25:MOV [ListPos],EDX ; The new position is calculated.
JMP .40:
.30:; Level is decreasing form EDX to EAX. Calculate the new value of LinePos.
ADD R12,RDX
BufferRetrieve [8*RAX+Level0Buffer::] ; Let RSI,RCX point to new LST_INDEX records.
SHR ECX,3
JZ .90:
XOR EDI,EDI ; Line counter in new buffer.
MOV EDX,[ListPos]
.35:INC EDI
LODSD ; EAX=LST_INDEX.FA
CMPB [R12+RAX],'#'
LODSD ; Skip LST_INDEX.Size
JNE .35:
DEC EDX
JNZ .35:
MOV [ListPos],EDI ; The new position is calculated.
.40:; Level is unchanged or adjusted.
MOV EAX,[Level]
BufferRetrieve [8*RAX+Level0Buffer::] ; Let RSI,RCX point to LST_INDEX records.
LEA R9,[RSI+RCX] ; R9=behind the last LST_INDEX record.
SHR ECX,3 ; SIZE# LST_INDEX = 8.
MOV [ListMax],ECX ; Number of lines in output.
MOV EDX,[ListPos]
TEST EDX
JZ .45:
DEC EDX
.45:LEA RBX,[RSI+8*RDX] ; RBX = pointer to LST_INDEX of ListPos-th record.
MOV EDX,2 ; RDX=Terminal row (2,3,,,TerminalHeight-1).
.50:; The loop for terminal lines.
MOV RAX,RDX ; Terminal row number.
LEA RDI,[AnsiAtRowCol+2]
StoD RDI
MOV EAX,';1H'
STOSD ; AnsiAtRowCol is prepared to move the cursor to row RDX, col 1.
StdOutput AnsiAtRowCol ; Make the position in terminal at column 1 of row RDX.
SUB ECX,ECX
MOV R13D,[TermWidth]
DEC R13 ; Decrease TermWidth due to ScrolBox.
CMP RBX,R9 ; At end of LST_INDEX?
JNB .55:
MOV ESI,[RBX+LST_INDEX.FA]
MOV ECX,[RBX+LST_INDEX.Size]
AND ECX,NoFlags
ADD RSI,[OutputPtr::]
CMP RCX,R13
JBE .55:
MOV RCX,R13 ; Line is long, its size will be limited to RCX=TermWidth. Number of following spaces will be R13=0
SUB R13,R13
JMP .60:
.55:SUB R13,RCX ; Line is short, its size will be RCX. Number of spaces will be R13.
.60:CALL .RowPaint ; RSI,RCX is the contents of the listing row. Size of following spaces is in R13.
ADD RBX,SIZE# LST_INDEX ; Line is painted. Go to paint the next line.
INC RDX
CMP EDX,[TermHeight]
JB .50:
; Scroll box.
MOV EDI,[TermHeight] ; Calculate ScrollHeight.
DEC EDI
MOV EAX,EDI
MUL RAX
MOV ECX,[ListMax]
TEST ECX
JZ .90:
DIV RCX
CMP EAX,0 ; Saturate ScrollHeight.
JA .63:
MOV EAX,1
.63:CMP EAX,EDI
JNA .67:
MOV EAX,EDI
.67:MOV ESI,EAX ; Let ESI=ScrollHeight.
MOV EAX,[ListPos] ; Calculate ScrollPos.
MUL RDI ; RDI=TermHeight-1.
MOV ECX,[ListMax]
DIV RCX
INC EAX
CMP EAX,EDI ; Saturate ScrollPos.
JNA .70:
MOV EAX,EDI
.70:MOV EDX,EAX ; EDX=ScrollPos, ESI=ScrollHeight.
ADD ESI,EDX ; ESI=position below the slider.
MOV EBX,[TermHeight]
DEC EBX
XOR ECX,ECX ; Row counter.
INC ECX
.75:INC ECX ; Paint the scrollbox at row ECX.
CMP ECX,EBX
JA .90:
LEA RDI,[AnsiAtRowCol+2]
MOV EAX,ECX
StoD RDI ; Row 2..TermHeight.
MOV AL,';'
STOSB
MOV EAX,[TermWidth]
DEC EAX
StoD RDI
MOV AX,'H'
STOSW
StdOutput AnsiAtRowCol ; Cursor is set at row ECX.
CMP ECX,EDX
JAE .85:
.80:StdOutput AnsiClrBgFEFEFF,ScrollBar
JMP .75:
.85:CMP ECX,ESI
JA .80:
StdOutput AnsiClrBgFEFEFF,ScrollSlider
JMP .75:
.90:RET
.RowPaint PROC ; Print a single row. Cursor was put at first column of the desired row.
; The row looks like
; "|dump | Remark in level0 Spaces" or
; "!### range ##### Name of the level3 Spaces"
; Input: RSI,RCX is the listing row (without CR+LF). RCX is never above TermWidth. R13 is the number of following spaces. RBX=^LST_INDEX
TEST RCX
JZ .8:
MOV R9,RSI
LEA RDI,[RSI+1] ; Characters at the beginning of line specify its level (color).
MOV AL,'#'
CMP [RDI],AL
JE .3: ; Jump when this row represents a colored level row (it starts with pipe+hash).
StdOutput AnsiClrBgC0C0C0; No, it is an ordinary Level0 row with a dump. Paint it gray.
MOV AL,'|'
MOV R8,RCX
REPNE SCASB ; Find the second pipe '|' which terminates the dump.
SUB RDI,RSI ; RDI is the dump size.
StdOutput RSI,Size=RDI ; Paint the dump. It is always displayed as unshorten.
ADD RSI,RDI ; RSI points to the start of the listing remark.
MOV RCX,R8 ; Restore RCX.
SUB RCX,RDI
JNA .9:
StdOutput AnsiClrBgFFFFD0; Continue in yellowish
JMP .7:
.3: LEA RDI,[AnsiClrBgD0D0FF]; blue
CMP [RSI+4],AL
JE .5:
LEA RDI,[AnsiClrBgFFD0D0]; red
CMP [RSI+3],AL
JE .5:
LEA RDI,[AnsiClrBgFFFF30]; yellow
CMP [RSI+2],AL
JE .5:
LEA RDI,[AnsiClrBgD0FFD0]; green
.5 StdOutput RDI ; Color1..4.
.7: StdOutput RSI,Size=RCX ; Paint the colorfull level row.
.8: StdOutput Spaces,Size=R13; Complete with spaces.
StdOutput AnsiClrBgC0C0C0; gray
.9: LEA RDI,[KeyBuffer]
MOV AX,0x5B1B ; Esc [
STOSW
MOV EAX,[RBX+LST_INDEX.Size]
SHR EAX,24 ; RCX=FA of found string related to start of line. 0 if no string was found at the line RBX.
JZ .99:
MOV RSI,R9
LEA RSI,[RSI+RAX-1]
StoD RDI
MOV AX,0x0047 ; G
STOSW
StdOutput KeyBuffer ; Put the cursor at column.
MOV ECX,[Find$Size]
JRCXZ .99:
StdOutput AnsiClrBgFFFFFF,AnsiClrFgFF00FF
StdOutput RSI,Size=RCX
StdOutput AnsiClrBgC0C0C0,AnsiClrFg000000
.99:RET
ENDP .RowPaint
ENDP WindowPaint
EuroView "input.bin" as BIN level 0. Found 0× "".
WindowTitle PROC
LEA RDI,[WorkMemory::] ; Title is constructed in WorkMemory.
MOV RDX,RDI
MOV AX,' '
STOSW
LEA RSI,[EuroView::]
MOV ECX,8
REP MOVSB
MOV AX,' "'
STOSW
LEA RSI,[ArgInputFile::]
.10:LODSB
CMP AL,0
JE .20:
STOSB
JMP .10:
.20:MOV AL,'"'
STOSB
MOV EAX,' as '
STOSD
MOV RSI,[FormatNamePtr::]
TEST RSI
JZ .50:
.30:LODSB
CMP AL,0
JE .40:
STOSB
JMP .30:
.40:LEA RSI,[=B' level ']
MOV CL,7
REP MOVSB
MOV AL,'0'
OR AL,[Level]
STOSB
LEA RSI,[=B'. Found ']
MOV CL,8
REP MOVSB
MOV EAX,[FoundCnt]
StoD RDI
MOV EAX,0x222097C3 ; × "
STOSD
LEA RSI,[Find$]
GetLength$ RSI
MOV [Find$Size],ECX
.45:LODSB
CMP AL,0
JE .48:
STOSB
JMP .45:
.48:MOV AX,'"'
STOSW
.50:MOVD [AnsiAtRowCol+2],'1;1H'
MOVB [AnsiAtRowCol+6],0
StdOutput AnsiClrBgFFFFFF,AnsiClrFg800000,AnsiAtRowCol
MOV EDX,[TermWidth]
StdOutput Spaces,Size=RDX
StdOutput AnsiAtRowCol,WorkMemory
.90:RET
ENDP WindowTitle
ListPos
WindowProc PROC
LEA RBX,[TermIO]
LinABI ioctl,0,TCGETS,RBX ; Load current local terminal status.
RstSt [RBX+TERMIO.c_lflag],ICANON+ECHO
LinABI ioctl,0,TCSETS,RBX ; Disable terminal echo.
StdOutput AnsiMouseEnable
JMP .ReadKeyboard:
.NoMore:
; Paint double frame around NoMore box:
StdOutput AnsiClrBgFFFFFF,AnsiClrFgFF00FF,AnsiRow4,AnsiCol4,AnsiBox2DR
MOV ECX,36
.M2:StdOutput AnsiBox2H
LOOP .M2:
StdOutput AnsiBox2DL,AnsiRow5,AnsiCol4,AnsiBox2V
StdOutput =B' Next|Previous occurence not found. ' ; NoMore text.
StdOutput AnsiBox2V,AnsiRow6,AnsiCol4,AnsiBox2UR
MOV ECX,36
.M3:StdOutput AnsiBox2H
LOOP .M3:
StdOutput AnsiBox2UL
LinABI nanosleep, TimeSpec1,0
.Resized:
CALL WindowResize
.Paint:
CALL WindowPaint
MOV EBX,[ListPos]
JMP .Saturate:
.AnsiKeyCtrlF:
.f:
.F: ; Find a text string in the output file. Position at the 1st occurence, if found.
CALL FindWindowPaint
CALL FindEditor ; Get string Find$.
JC .Paint: ; CF=cancel. Repaint screen; do not search.
PUSH RBX
CALL Search ; Mark LST_INDEX.Size with FAFound bits.
POP RBX
CALL WindowTitle
MOV EDX,[Level] ; 0..4.
BufferRetrieve [8*RDX+Level0Buffer::] ; RSI,RCX is an array of LST_INDEX.
LEA RDX,[RSI+RCX] ; End of the array.
MOV EBX,-1 ; ListPos.
SUB RSI,SIZE# LST_INDEX
.FF:INC EBX
ADD RSI,SIZE# LST_INDEX
CMP RSI,RDX
JNB .NoMore: ; No more found.
MOV ECX,[RSI+LST_INDEX.Size]
SHR ECX,24
JZ .FF:
INC EBX
JMP .Saturate:
.AnsiKeyCtrlN:
.n:
.N: ; Find the next occurence.
MOV EDX,[Level] ; 0..4.
BufferRetrieve [8*RDX+Level0Buffer::] ; RSI,RCX is an array of LST_INDEX.
LEA RDX,[RSI+RCX] ; End of the array.
MOV EBX,[ListPos]
LEA RSI,[RSI+8*RBX]
.NF:INC EBX
ADD RSI,SIZE#LST_INDEX
CMP RSI,RDX
JNB .NoMore: ; No more found.
MOV ECX,[RSI+LST_INDEX.Size]
SHR ECX,24
JZ .NF:
INC EBX
JMP .Saturate:
.AnsiKeyCtrlP:
.p:
.P: ; Find the previous occurence.
MOV EDX,[Level]
BufferRetrieve [8*RDX+Level0Buffer::] ; RSI,RCX is an array of LST_INDEX.
MOV RDX,RSI ; Begining of the array.
MOV EBX,[ListPos]
DEC EBX
LEA RSI,[RSI+8*RBX]
.PF:DEC EBX
JZ .NoMore:
SUB RSI,SIZE#LST_INDEX
CMP RSI,RDX
JB .NoMore:
MOV ECX,[RSI+LST_INDEX.Size]
SHR ECX,24
JZ .PF:
INC EBX
JMP .Saturate:
.ReadKeyboard:
LinABI ioctl,0,TIOCGWINSZ,TtySize ; Check if terminal dimension changed.
MOVZXW EDX,[TtySize.ts_lines]
MOVZXW ECX,[TtySize.ts_cols]
CMP EDX,[TermHeight]
JNE .Resized:
CMP ECX,[TermWidth]
JNE .Resized:
XOR EDI,EDI ; Read the keyboard in the main window.
LEA RSI,[KeyBuffer]
MOV R8,RSI
MOV [RSI],RDI
MOV EDX,SIZE# KeyBuffer
LinABI read,RDI,RSI,RDX
TEST EAX
JNA .ReadKeyboard:
XOR ECX,ECX
MOV EBX,[ListPos]
MOV DL,[RSI]
Dispatch DL,'f','F','n','N','p','P'
key %FOR AnsiKeyCtrlHome,AnsiKeyCtrlEnd,AnsiKeyPgUp,AnsiKeyPgDn,AnsiKeyUp,AnsiKeyDn, \ Read the keyboard.
AnsiKeyHome,AnsiKeyEnd,AnsiKeyShiftTab,AnsiKeyTab,AnsiKeyCtrlF,AnsiKeyCtrlP,AnsiKeyCtrlN,AnsiKeyEscape
LEA RDI,[%key]
MOV CL,SIZE# %key
MOV RSI,R8
REPE CMPSB
JE .%key:
%ENDFOR key
MOV RSI,R8 ; Read the mouse.
LODSW
CMP AX,0x5B1B ; Esc [
JNE .ReadKeyboard:
LODSB
CMP AL,0x3C ; Less-than?
JNE .ReadKeyboard:
LodD ; Mouse button.
JC .ReadKeyboard:
DEC EBX ; ListPos.
CMP AL,64 ; Scroll wheel up?
JE .Saturate:
ADD EBX,1+1
CMP AL,65 ; Scroll wheel down?
JE .Saturate:
MOV ECX,EAX ; Not scrolled. CL=0 for left button; CL=2 for right button.
LODSB
CMP AL,';'
JNE .ReadKeyboard:
LodD
JC .ReadKeyboard:
MOV EBX,EAX ; X-coordinate of mouse cursor.
SHL EBX,16
LODSB
CMP AL,';'
JNE .ReadKeyboard:
LodD
JC .ReadKeyboard:
MOV BX,AX ; Y-coordinate of mouse cursor.
LODSB
CMP AL,'m'
JNE .ReadKeyboard:
; Mouse cursor coordinates are in EBX. Row number is in BX, button in CL.
MOV EDX,EBX
SHR EDX,16
MOV EAX,[TermWidth]
SUB EAX,3
CMP DX,AX
JA .Scroll:
CMP CL,0
JE .MouseLeftButton:
CMP CL,2
JE .MouseRightButton:
JMP .ReadKeyboard:
.Scroll: ; Clicked at scroll box, row BX. Level is not influenced by mouse buttons.
MOVZX EAX,BX
TEST EAX
JZ .30:
DEC EAX
.30:MOV ECX,[ListMax]
MUL RCX
MOV ECX,[TermHeight]
DIV RCX
MOV [ListPos],EAX
CALL WindowPaint
JMP .ReadKeyboard:
.AnsiKeyUp:
DEC EBX
.Saturate:TEST EBX ; Saturate: Keep EBX in the range 0..ListMax-1.
JNS .60:
SUB EBX,EBX
.60:CMP EBX,[ListMax]
JB .70:
MOV EBX,[ListMax]
.70:TEST EBX
JNZ .75:
INC EBX
.75:MOV [ListPos],EBX
CALL WindowPaint
JMP .ReadKeyboard:
.AnsiKeyDn:
INC EBX
JMP .Saturate:
.AnsiKeyPgUp:
SUB EBX,[TermHeight]
ADD EBX,2
JMP .Saturate:
.AnsiKeyPgDn:
ADD EBX,[TermHeight]
SUB EBX,2
JMP .Saturate:
.AnsiKeyCtrlHome:
.AnsiKeyHome:
MOV EBX,0
JMP .Saturate:
.AnsiKeyCtrlEnd:
.AnsiKeyEnd:
MOV EBX,[ListMax::]
SUB EBX,[DirHeight]
ADD EBX,2
JMP .Saturate:
.MouseRightButton: ; Increase level.
.AnsiKeyShiftTab: ; Shift Tab pressed.
MOV EAX,[Level]
CMP AL,4
JAE .ReadKeyboard:
INC AL
.80:MOV [Level],EAX
CALL WindowPaint
JMP .ReadKeyboard:
.MouseLeftButton: ; Decrease level.
.AnsiKeyTab: ; Tab pressed.
MOV EAX,[Level]
CMP EAX,0
JLE .ReadKeyboard:
DEC AL
JMP .80:
ErrorHelp:
StdOutput Help:,Eol=yes
TerminateProgram 8
ErrorFileOutput:
LEA RSI,[FileOutput.Name]
JMP ErrorFile:
ErrorFileInput:
LEA RSI,[FileInput.Name]
JMP ErrorFile:
ErrorFileIndex:
LEA RSI,[FileOutput.Name]
ErrorFile:
StdOutput =B'File "',RSI,=U'" not found or empty.',Eol=yes
JMP .AnsiKeyEscape:
ErrorAlloc::
StdOutput =B"Error on memory allocation.",Eol=yes
JMP .AnsiKeyEscape:
.AnsiKeyEscape:
FileClose FileInput, FileOutput, FileIndex
LEA RBX,[TermIO]
LinABI ioctl,0,TCGETS,RBX ; Get current terminal echo.
SetSt [RBX+TERMIO.c_lflag],ICANON+ECHO
LinABI ioctl,0,TCSETS,RBX ; Restore terminal echo.
RET
ENDP WindowProc
CreateLstIndex: PROC
BufferClear [Level0Buffer::],[Level1Buffer::],[Level2Buffer::],[Level3Buffer::],[Level4Buffer::]
MOV RBX,[OutputPtr::] ; Start of the listing in memory.
XOR ECX,ECX
MOV AL,10 ; EOL character.
DEC RCX
MOV RDX,RBX
.10:MOV RDI,RDX ; Start of physical line.
CMP RDI,[OutputEnd::]
JNB .80:
MOV RSI,RDI
REPNE SCASB ; Find the end of line.
MOV RDX,RDI
SUB RDI,RSI ; Size.
SUB RSI,RBX ; FA.
SUB EDI,2 ; Omit CR+LF.
MOV [LstIndex.FA],ESI
MOV [LstIndex.Size],EDI ; Line size (it is never above 0x0FFF_FFFF).
BufferStore [Level0Buffer::],LstIndex,SIZE# LstIndex
JC ErrorAlloc::
CMPB [RBX+RSI+1],'#' ; Is the line in Level1?
JNE .10:
BufferStore [Level1Buffer::],LstIndex,SIZE# LstIndex
JC ErrorAlloc::
CMPW [RBX+RSI+1],'##' ; Is the line in Level2?
JNE .10:
BufferStore [Level2Buffer::],LstIndex,SIZE# LstIndex
CMPB [RBX+RSI+3],'#' ; Is the line in Level3?
JNE .10:
BufferStore [Level3Buffer::],LstIndex,SIZE# LstIndex
CMPD [RBX+RSI+1],'####' ; Is the line in Level4?
JNE .10:
BufferStore [Level4Buffer::],LstIndex,SIZE# LstIndex
JMP .10:
.80:BufferRetrieve [Level0Buffer::]
SHR ECX,3 ; SIZE# LstIndex = 8
MOV [ListingLines::],ECX
CLC
RET
ENDP CreateLstIndex
4, 4, 8, TermWidth-4
with the string FindEdit initialized from Find$.
FindWindowPaint PROC
MOV EAX,[TermWidth] ; 0x7F
SUB EAX,12
MOV [FindEditWidth],EAX ; 0x67
LEA RSI,[Find$] ; Copy Find$ to FindEdit.
LEA RDI,[FindEdit]
.05:LODSB
STOSB
CMP AL,0
JNE .05:
; Paint double frame around Find box:
StdOutput AnsiClrBgFFFFFF,AnsiClrFg000000,AnsiRow4, AnsiCol4,AnsiBox2DR
MOV ECX,[TermWidth]
MOVB [FindEdit+RCX-3],0
SUB ECX,9
MOV EDX,ECX
.10:StdOutput AnsiBox2H
LOOP .10:
StdOutput AnsiBox2DL,AnsiRow5,AnsiCol4,AnsiBox2V
StdOutput Spaces,Size=RDX
StdOutput AnsiBox2V,AnsiRow6,AnsiCol4,AnsiBox2V
StdOutput Spaces,Size=RDX
StdOutput AnsiBox2V,AnsiRow7,AnsiCol4,AnsiBox2UR
MOV ECX,EDX
.20:StdOutput AnsiBox2H
LOOP .20:
StdOutput AnsiBox2UL,AnsiRow5,AnsiCol6,AnsiClrFg808080,=B'Enter the text string to find. Then use keys N|P to find next|previous occurence'
StdOutput AnsiRow6,AnsiCol6,AnsiClrFgFF00FF,FindEdit
RET
ENDP FindWindowPaint
FindEditor PROC
GetLengthUTF8 FindEdit
MOV [FindEditLen],ECX
MOV [FindCursor],ECX
.Read:
StdOutput AnsiRow6,AnsiCol6,FindEdit
CALL .ShowCursor:
XOR EDI,EDI ; Read the keyboard in the edit window.
LEA RSI,[KeyBuffer]
MOV R8,RSI
MOV [RSI],RDI
MOV EDX,SIZE# KeyBuffer
LinABI read,RDI,RSI,RDX
TEST EAX
JNA .Read:
XOR ECX,ECX ; Key is at RSI=R8, its size=RAX.
key %FOR AnsiKeyEnter,AnsiKeyLineFeed,AnsiKeyEscape,AnsiKeyLeft,AnsiKeyRight,AnsiKeyBackspace, \
AnsiKeyDelete,AnsiKeyDelete2,AnsiKeyHome,AnsiKeyEnd,AnsiKeyCtrlDelete
LEA RDI,[%key]
MOV CL,SIZE# %key
MOV RSI,R8
REPE CMPSB
JE .%key:
%ENDFOR key
MOV RSI,R8 ; Special keys were dispatched.
CMPB [RSI],27
JE .Read: ; Ignore other characters which start with Esc.
; RSI points to a pressed ordinary key (letter). RAX is its UTF-8 size 1..4.
; Make room for RAX bytes at [FindCursor] by right-shift the text between [FindCursor]..[FindCursorSize+RAX].
PUSH RAX,RSI
LEA RDX,[FindEdit]
MOV RSI,RDX
GetLength$ RDX ; Return RCX=size of text in FindEdit up to NUL in bytes.
ADD RSI,RCX
LEA RDI,[RSI+RAX]
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
ADD RDX,RCX
LEA RCX,[RSI+1]
SUB RCX,RDX
STD
REP MOVSB
CLD
POP RSI,RAX
CALL .Cursor2Pos: ; Return RCX=FindEditSize in bytes corresponding to FindEditLen.
LEA RDI,[FindEdit+RCX]
MOV RCX,RAX
REP MOVSB
INC [FindCursor]
JMP .Read:
.AnsiKeyCtrlDelete: ; Erase all FileEdit field.
MOVB [FindEdit],0
StdOutput AnsiRow6,AnsiCol6
MOV ECX,TermWidth
SUB EDX,6+6
StdOutput Spaces,Size=RDX
MOV [FindCursor],0
CALL .ShowCursor
JMP .Read:
.AnsiKeyHome:
MOV [FindCursor],0
JMP .Read:
.AnsiKeyEnd:
GetLengthUTF8 FindEdit
MOV [FindCursor],ECX
JMP .Read:
.AnsiKeyLeft:
MOV EAX,[FindCursor]
DEC EAX
JS .Read:
MOV [FindCursor],EAX
CALL .ShowCursor:
JMP .Read:
.AnsiKeyRight:
CALL .Cursor2Pos:
LEA RSI,[FindEdit+RCX]
LODSB
CMP AL,0
JNE .R1:
MOVB [RSI-1],' '
.R1:MOV EAX,[FindCursor]
INC EAX
CMP EAX,[FindEditWidth]
JA .Read:
MOV [FindCursor],EAX
CALL .ShowCursor:
JMP .Read:
.AnsiKeyDelete:
INC [FindCursor]
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RSI,[FindEdit+RCX]
DEC [FindCursor]
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RDI,[FindEdit+RCX]
LEA RCX,[FindEdit+SIZE#FindEdit+1]
SUB RCX,RSI
REP MOVSB
CALL .ShowField:
JMP .Read:
.AnsiKeyDelete2:
.AnsiKeyBackspace:
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RSI,[FindEdit+RCX]
MOV EAX,[FindCursor]
DEC EAX
JS .Read:
MOV [FindCursor],EAX
CALL .Cursor2Pos: ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
LEA RDI,[FindEdit+RCX]
LEA RCX,[FindEdit+SIZE#FindEdit+1]
SUB RCX,RSI
REP MOVSB
CALL .ShowField:
JMP .Read:
.AnsiKeyLineFeed:
.AnsiKeyEnter: ; Save changes to Find$.
LEA RSI,[FindEdit]
LEA RDI,[Find$]
.E1:LODSB
STOSB
CMP AL,0
JNE .E1:
.90:GetLength$ Find$
MOV [Find$Size],ECX
RET ; Return CF=0.
.AnsiKeyEscape: ; Ignore changes.
STC
RET ; Return CF=1.
.ShowField: PROC ; Display FindEdit up to NUL and then spaces.
MOV EDX,[TermWidth]
SUB EDX,6+6
LEA RDI,[FindEdit]
StdOutput AnsiRow6,AnsiCol6,RDI
GetLength$ RDI
SUB EDX,ECX
JNA .9:
StdOutput Spaces,Size=RDX
.9: RET
ENDP .ShowField:
.Cursor2Pos: PROC ; Return RCX=position in bytes in FindEdit corresponding with [FindCursor].
PUSH RAX,RSI
LEA RSI,[FindEdit]
MOV ECX,[FindCursor] ; Length in characters 0..
JRCXZ .9:
.0: LODSB ; Load UTF-8 character.
OR AL,0000_0111b
CMP AL,1111_0111b
JE .4 ; Three more bytes follow.
OR AL,0000_1111b
CMP AL,1110_1111b
JE .3: ; Two more bytes follow.
OR AL,0001_1111b
CMP AL,1101_1111b
JE .2: ; One more byte follows.
JMP .1: ; Otherwise no extra byte follows.
.4: INC RSI
.3: INC RSI
.2: INC RSI
.1: LOOP .0:
.9: LEA RCX,[FindEdit]
XCHG RCX,RSI
SUB RCX,RSI
POP RSI,RAX
RET
ENDP .Cursor2Pos:
.ShowCursor: PROC ; Display cursor at [FindCursor]. Clobbers RAX,RDI.
LEA RDI,[KeyBuffer]
MOV AX,0x5B1B ; Esc [
STOSW
MOV EAX,[FindCursor] ; 0..
ADD EAX,6 ; Offset of 0-th character from left screen edge.
StoD RDI
MOV AX,0x0047 ; G
STOSW
StdOutput KeyBuffer
RET
ENDP .ShowCursor:
ENDP FindEditor
Search PROC
XOR EAX,EAX
MOV [FoundCnt],EAX
CMPB [Find$],AL
JE .90:
lvl %FOR 0..4 ; The string Find$ is not empty. Search all five buffers.
MOV RBX,[Level%lvl{}Buffer]
CALL .SearchBuffer:
%ENDFOR
.90: RET
.SearchBuffer: PROC
BufferRetrieve RBX ; RSI,RCX is array of LST_INDEX records.
MOV R15,RSI ; Beginning of buffer contents.
LEA R14,[RSI+RCX] ; End of buffer contents.
MOV R13,[OutputPtr::] ; Start of output file contents mapped to memory.
LEA RSI,[Find$] ; The needle to search for.
MOV ECX,[Find$Size]
TEST ECX
JZ .90:
LODSB ; AL is 1st letter of the needle.
MOV R12,RSI ; Pointer to needle plus one.
LEA R11,[RCX-1] ; Size of needle minus one.
.10:CMP R15,R14
JNB .90: ; When at end of buffer.
MOV EDI,[R15+LST_INDEX.FA]
MOV ECX,[R15+LST_INDEX.Size]
MOV R10,RDI ; FA of start of the line.
ADD RDI,R13
AND ECX,NoFlags
MOV [R15+LST_INDEX.Size],ECX ; Clear FAFound.
.30:REPNE SCASB ; Find the 1st letter of needle=Find$ in RDI,RCX.
JNE .70:
PUSH RDI,RCX ; 1st letter was found. Compare the rest of needle.
MOV RCX,R11
MOV RSI,R12
REPE CMPSB
POP RCX,RDI
JNE .30:
SUB RDI,R13 ; Find$ was found.
SUB RDI,R10
OR [R15+LST_INDEX.Size+3],DIL ; Mark its offset in LST_INDEX record.
CMP RBX,[Level0Buffer]
JNE .70:
INC [FoundCnt]
.70:ADD R15,SIZE# LST_INDEX ; Index of the next output line.
JMP .10:
.90:RET
ENDP .SearchBuffer:
ENDP Search
%IF %^DEBUG INCLUDE1 linabi.htm DebugOutput PROC StdOutput RSI,Size=RCX RET ENDP DebugOutput %ENDIF ENDPROGRAM viewling