////////////////////////////////////////////////////////////////////// // // // observerHexEditor.pas: Memory viewer and editor // // // // The contents of this file are subject to the Bottled Light // // Public License Version 1.0 (the "License"); you may not use this // // file except in compliance with the License. You may obtain a // // copy of the License at http://www.bottledlight.com/BLPL/ // // // // Software distributed under the License is distributed on an // // "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or // // implied. See the License for the specific language governing // // rights and limitations under the License. // // // // The Original Code is the Mappy VM User Interface, released // // April 1st, 2003. The Initial Developer of the Original Code is // // Bottled Light, Inc. Portions created by Bottled Light, Inc. are // // Copyright (C) 2001-2003 Bottled Light, Inc. All Rights Reserved. // // // // Author(s): // // Michael Noland (joat), michael@bottledlight.com // // // // Changelog: // // 1.0: First public release (April 1st, 2003) // // // // Notes: // // None at present. // // // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// unit observerHexEditor; ////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// interface //////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IniFiles, CpuObservers, Menus, debugFindPattern, Math, console, nexus, AddressSpace, ComCtrls, ToolWin; ////////////////////////////////////////////////////////////////////// type THexEditorMode = (hemStream, hemByte, hemHalfword, hemWord); TjdevHexEditor = class(TCpuObserver) scrollbar: TScrollBar; popupMenu: TPopupMenu; mBrowseTo: TMenuItem; mReturntoPC: TMenuItem; N2: TMenuItem; mDisplayBIOS: TMenuItem; mDisplayExWRAM: TMenuItem; mDisplayWRAM: TMenuItem; mDisplayPalette: TMenuItem; mDisplayOAM: TMenuItem; mDisplayVRAM: TMenuItem; mDisplayROM: TMenuItem; editor: TEdit; mViewStack: TMenuItem; mDisplaySRAM: TMenuItem; saveDialog: TSaveDialog; openDialog: TOpenDialog; toolbar: TToolBar; Spacer1: TToolButton; rbByte: TRadioButton; rbHalfword: TRadioButton; rbWord: TRadioButton; Spacer2: TToolButton; eAddress: TComboBox; bGoto: TToolButton; rbStream: TRadioButton; bTools: TToolButton; mFindPattern: TMenuItem; mFindAgain: TMenuItem; N1: TMenuItem; mLoadIntoMemory: TMenuItem; mSaveMemory: TMenuItem; N3: TMenuItem; bHelp: TToolButton; bFind: TToolButton; fontDialog: TFontDialog; N4: TMenuItem; mChangeFont: TMenuItem; procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure scrollbarChange(Sender: TObject); procedure FormPaint(Sender: TObject); procedure FormResize(Sender: TObject); procedure FormMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean); procedure FormMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure editorKeyPress(Sender: TObject; var Key: Char); procedure ChangeDisplayMode(Sender: TObject); procedure ChangeCurrentBank(Sender: TObject); procedure GotoAddress(Sender: TObject); procedure addressKeyPress(Sender: TObject; var Key: Char); procedure BrowseTo(Sender: TObject); procedure ReturnToPC(Sender: TObject); procedure ReturnToSP(Sender: TObject); procedure DisplayBIOS(Sender: TObject); procedure DisplayExWRAM(Sender: TObject); procedure DisplayWRAM(Sender: TObject); procedure DisplayPalette(Sender: TObject); procedure DisplayVRAM(Sender: TObject); procedure DisplayOAM(Sender: TObject); procedure DisplayROM(Sender: TObject); procedure DisplaySRAM(Sender: TObject); procedure LoadRawMemory(Sender: TObject); procedure SaveRawMemory(Sender: TObject); procedure FindPattern(Sender: TObject); procedure FindPatternAgain(Sender: TObject); procedure ShowMenu(Sender: TObject); procedure ShowHelp(Sender: TObject); procedure ChangeFont(Sender: TObject); procedure FormDestroy(Sender: TObject); protected FEditingAt: uint32; findingAgain: boolean; baseAddress: uint32; fontString: string; displayMode: THexEditorMode; procedure FinishEditing; procedure SetAddress(addr: uint32); public procedure UpdateObserver; override; class function OCaption: string; override; procedure LoadSettings(ini: TIniFile); override; procedure SaveSettings(ini: TIniFile); override; end; ////////////////////////////////////////////////////////////////////// var jdevHexEditor: TjdevHexEditor; ////////////////////////////////////////////////////////////////////// implementation /////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// {$R *.DFM} ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormCreate(Sender: TObject); begin HelpContext := LinkHelp('memory_editor.html'); editor.visible := false; findingAgain := false; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormShow(Sender: TObject); begin eAddress.Text := '$' + IntToHex(vmCurrentPC, 8); SetAddress(vmCurrentPC); if fontString <> '' then StringToFont(fontString, Font); // Load the translation LoadTranslation(self, translation); end; ////////////////////////////////////////////////////////////////////// class function TjdevHexEditor.OCaption: string; begin Result := 'Memory Editor'; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.UpdateObserver; var addr: uint32; i, y: integer; data: array[0..16] of byte; st: string; mirror: boolean; numLines: integer; cartSize1, cartSize2: uint32; banks: TvmMemoryLock1; begin Constraints.MinWidth := 0; Constraints.MaxWidth := 0; case displayMode of hemStream: ClientWidth := canvas.TextWidth('@')*(28 + 32) + scrollbar.width; hemByte: ClientWidth := canvas.TextWidth('@')*(28 + 47) + scrollbar.width; hemHalfword: ClientWidth := canvas.TextWidth('@')*(28 + 39) + scrollbar.width; hemWord: ClientWidth := canvas.TextWidth('@')*(28 + 35) + scrollbar.width; end; Constraints.MinWidth := Width; Constraints.MaxWidth := Width; numLines := 0; y := toolbar.height; addr := baseAddress; while (y < clientHeight) and (addr < $10000000) do begin st := IntToHex(addr, 8) + ' '; // addr := addr and $0FFFFFFF; vmLockMemory(banks); cartSize1 := Min(banks.romsize, 1 shl 24); cartSize2 := banks.romsize - cartSize1; vmUnlockMemory(banks); case (addr shr 24) and $F of $0,$1: mirror := addr >= $00000000 + SYSTEM_ROM_MASK; $2: mirror := addr > $02000000 + EX_WRAM_MASK; $3: mirror := addr > $03000000 + WRAM_MASK; $5: mirror := addr > $05000000 + PALETTE_MASK; $6: mirror := addr >= $06014000; $7: mirror := addr > $07000000 + OAM_MASK; $8: mirror := addr >= $08000000 + cartSize1; $A: mirror := addr >= $0A000000 + cartSize1; $C: mirror := addr >= $0C000000 + cartSize1; $9: mirror := addr >= $09000000 + cartSize2; $B: mirror := addr >= $0B000000 + cartSize2; $D: mirror := addr >= $0D000000 + cartSize2; $E: mirror := addr > $0E000000 + SRAM_MASK; $F: mirror := addr > $0F000000 + SRAM_MASK; else mirror := false; end; canvas.brush.Color := clBtnFace; if mirror then canvas.Font.Color := clGrayText else canvas.Font.Color := clBtnText; // Get the data case displayMode of hemStream: begin st := st + ' '; for i := 0 to 15 do begin data[i] := vmReadByte(addr); Inc(addr); st := st + IntToHex(data[i], 2); end; end; hemByte: for i := 0 to 15 do begin data[i] := vmReadByte(addr); Inc(addr); st := st + ' ' + IntToHex(data[i], 2); end; hemHalfword: for i := 0 to 15 do begin data[i] := vmReadByte(addr); Inc(addr); if i and 1 = 1 then st := st + ' ' + IntToHex(Puint16(@(data[i and not 1]))^, 4); end; hemWord: for i := 0 to 15 do begin data[i] := vmReadByte(addr); Inc(addr); if i and 3 = 3 then st := st + ' ' + IntToHex(Puint32(@(data[i and not 3]))^, 8); end; end; // Add the ASCII dump on the right for i := 0 to 15 do if data[i] < 32 then data[i] := Ord('.'); data[16] := 0; st := st + ' ' + Pchar(@(data[0])); // Draw it canvas.TextOut(0, y, st); y := y + canvas.TextHeight(st); Inc(numLines); end; scrollbar.LargeChange := Max(numLines-1, 1)*16; if (addr > $10000000) and (st <> '') then begin numLines := Height div canvas.TextHeight(st)-1; baseAddress := $10000000 - uint32(Max(numLines, 1)*16); Repaint; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormPaint(Sender: TObject); begin // Font.Assign(hexEditorFont); // editor.Font.Assign(hexEditorFont); if not editor.visible then editor.Top := -editor.height; editor.Width := canvas.TextWidth('@')*2; editor.Height := canvas.TextHeight('@'); UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormResize(Sender: TObject); begin UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean); begin SetAddress(Min(scrollbar.Position + 16, scrollbar.Max)); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean); begin SetAddress(Max(scrollbar.Position - 16, scrollbar.Min)); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.SetAddress(addr: uint32); begin scrollbar.Position := addr and $0FFFFFF0; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.scrollbarChange(Sender: TObject); begin if uint32(scrollbar.Position) <> baseAddress then FinishEditing; baseAddress := scrollbar.Position; UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var w, h: integer; begin if editor.visible then FinishEditing else if Button = mbLeft then begin w := canvas.TextWidth('@'); h := canvas.TextHeight('@'); Y := (Y-toolbar.height) div h; X := X div w - 10; case displayMode of hemStream: begin X := X div 2; if (X >= 0) and (X < 16) then begin FEditingAt := baseAddress + uint32(Y*16 + X); editor.text := ''; editor.Left := (X*2+10)*w; editor.Top := h*Y + toolbar.height; editor.Visible := true; end; end; hemByte: if X and 3 <> 3 then begin X := X div 3; if (X >= 0) and (X < 16) then begin FEditingAt := baseAddress + uint32(Y*16 + X); editor.text := ''; editor.Left := (X*3+10)*w; editor.Top := h*Y + toolbar.height; editor.Visible := true; end; end; hemHalfword: if X mod 5 <> 4 then begin X := (X-((X+1) div 5)) div 2; if (X >= 0) and (X < 16) then begin FEditingAt := baseAddress + uint32(Y*16 + X xor 1); editor.text := ''; editor.Left := ((X shr 1)*5 + (X and 1)*2 + 10)*w; editor.Top := h*Y + toolbar.height; editor.Visible := true; end; end; hemWord: if X mod 9 <> 8 then begin X := (X-((X+1) div 9)) div 2; if (X >= 0) and (X < 16) then begin FEditingAt := baseAddress + uint32(Y*16 + X xor 3); editor.text := ''; editor.Left := ((X shr 2)*9 + (X and 3)*2 + 10)*w; editor.Top := h*Y + toolbar.height; editor.Visible := true; end; end; end; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.editorKeyPress(Sender: TObject; var Key: Char); begin if (key = #10) or (key = #13) then begin FinishEditing; Key := #0; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FinishEditing; var st: string; begin if editor.visible then begin st := Trim(editor.Text); if (Length(st) > 0) and (st[1] <> '$') then st := '$' + st; vmWriteByte(FEditingAt, StrToIntDef(st, vmReadByte(FEditingAt))); editor.Visible := false; editor.Top := -editor.height; UpdateObserver; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var shiftFactor: integer; begin if ssShift in Shift then shiftFactor := 10 else shiftFactor := 1; case Key of VK_PRIOR: SetAddress(Max(scrollbar.Position - (scrollBar.LargeChange * shiftFactor), scrollbar.Min)); VK_NEXT: SetAddress(Min(scrollbar.Position + (scrollBar.LargeChange * shiftFactor), scrollbar.Max)); end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.ChangeDisplayMode(Sender: TObject); begin if rbByte.Checked then displayMode := hemByte else if rbHalfword.Checked then displayMode := hemHalfword else if rbWord.Checked then displayMode := hemWord else displayMode := hemStream; if editor.visible then FinishEditing; UpdateObserver; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.ChangeCurrentBank(Sender: TObject); begin case eAddress.ItemIndex of 0: SetAddress($00000000); 1: SetAddress($02000000); 2: SetAddress($03000000); 3: SetAddress($04000000); 4: SetAddress($05000000); 5: SetAddress($06000000); 6: SetAddress($07000000); 7: SetAddress($08000000); 8: SetAddress($0E000000); end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.GotoAddress(Sender: TObject); var st: string; i: integer; begin st := eAddress.Text; i := Pos(' ', st); if i > 0 then Delete(st, i, Length(st)-i); if Length(st) > 0 then begin if st[1] <> '$' then st := '$' + st; SetAddress(StrToIntDef(st, scrollbar.Position)); eAddress.Text := '$' + IntToHex(uint32(scrollbar.Position), 8); end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.addressKeyPress(Sender: TObject; var Key: Char); begin if (key = #10) or (key = #13) then begin GotoAddress(Sender); Key := #0; end; end; ////////////////////////////////////////////////////////////////////// // Context menu ////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.BrowseTo(Sender: TObject); var st: string; begin try st := InputBox('Mappy VM', 'Enter the address (in hex) to display', Format('$%8.8x', [scrollbar.Position])); if Length(st) > 0 then begin if st[1] <> '$' then st := '$' + st; SetAddress(StrToIntDef(st, scrollbar.Position)); end; except on e: EConvertError do e.Free; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.ReturnToPC(Sender: TObject); begin SetAddress(vmCurrentPC); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.ReturnToSP(Sender: TObject); begin SetAddress(vmGetRegister(13)); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplayBIOS(Sender: TObject); begin SetAddress($00000000); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplayExWRAM(Sender: TObject); begin SetAddress($02000000); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplayWRAM(Sender: TObject); begin SetAddress($03000000); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplayPalette(Sender: TObject); begin SetAddress($05000000); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplayVRAM(Sender: TObject); begin SetAddress($06000000); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplayOAM(Sender: TObject); begin SetAddress($07000000); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplayROM(Sender: TObject); begin SetAddress($08000000); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.DisplaySRAM(Sender: TObject); begin SetAddress($0E000000); end; ////////////////////////////////////////////////////////////////////// // Advanced Menu ///////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FindPattern(Sender: TObject); begin dbgFindPattern.bFind.OnClick := FindPatternAgain; findingAgain := false; dbgFindPattern.Show; end; ////////////////////////////////////////////////////////////////////// function IncAddress(var addr: uint32; romSize: uint32): boolean; begin Result := false; Inc(addr); case (addr shr 24) and $F of $0,$1: if addr > $00000000 + SYSTEM_ROM_MASK then addr := $02000000; $2: if addr > $02000000 + EX_WRAM_MASK then addr := $03000000; $3: if addr > $03000000 + WRAM_MASK then addr := $04000000; $4: if addr > $04000000 + $804 then addr := $05000000; $5: if addr > $05000000 + PALETTE_MASK then addr := $06000000; $6: if addr > $06013FFF then addr := $07000000; $7: if addr > $07000000 + OAM_MASK then addr := $08000000; $8..$D: if addr >= $08000000 + romSize then addr := $0E000000; $E..$F: if addr > $0E00FFFF then begin Result := not (MessageDlg('Reached the top of memory, continue from the bottom?', mtConfirmation, [mbYes, mbNo], 0) = mrYes); addr := $00000000; end; end; end; ////////////////////////////////////////////////////////////////////// function DecAddress(var addr: uint32; romSize: uint32): boolean; begin Result := false; case (addr shr 24) and $F of $0..$1: if addr = $00000000 then begin Result := not (MessageDlg('Reached the bottom of memory, continue from the top?', mtConfirmation, [mbYes, mbNo], 0) = mrYes); addr := $0E00FFFF; end else Dec(addr); $2: if addr = $02000000 then addr := SYSTEM_ROM_MASK else Dec(addr); $3: if addr = $03000000 then addr := $02000000+EX_WRAM_MASK else Dec(addr); $4: if addr = $04000000 then addr := $03000000+WRAM_MASK else Dec(addr); $5: if addr = $05000000 then addr := $04000803 else Dec(addr); $6: if addr = $06000000 then addr := $05000000+PALETTE_MASK else Dec(addr); $7: if addr = $07000000 then addr := $06013FFF else Dec(addr); $8..$D: if addr = $08000000 then addr := $07000000+OAM_MASK else Dec(addr); $E..$F: if addr = $0E000000 then addr := $08000000+romSize else Dec(addr); end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FindPatternAgain(Sender: TObject); var banks: TvmMemoryLock1; addr, backup: uint32; matchIndex: integer; pattern: Puint8array; patternLength: integer; done: boolean; begin if dbgFindPattern.data = '' then begin Beep; Exit; end; pattern := @(dbgFindPattern.data[1]); patternLength := Length(dbgFindPattern.data); addr := dbgFindPattern.address; backup := addr; matchIndex := 0; if findingAgain then begin if dbgFindPattern.goDown then DecAddress(addr, banks.romsize) else IncAddress(addr, banks.romsize); end; findingAgain := true; repeat // Read a byte and test against the pattern if vmReadByte(addr) = pattern^[matchIndex] then begin if matchIndex = 0 then backup := addr; Inc(matchIndex); end else begin if matchIndex > 0 then addr := backup; matchIndex := 0; end; // Advance the address if matchIndex = patternLength then done := true else begin if dbgFindPattern.goDown and (matchIndex = 0) then done := DecAddress(addr, banks.romsize) else done := IncAddress(addr, banks.romsize); end; until done; // Display the search results if matchIndex > 0 then begin addr := backup; SetAddress(addr); MessageDlg(Format('Pattern found at $%.8x.', [addr]), mtInformation, [mbOk], 0); Beep; end; dbgFindPattern.address := addr; dbgFindPattern.cbBaseAddress.Text := '$' + IntToHex(addr, 8); dbgFindPattern.Hide; vmUnlockMemory(banks); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.LoadRawMemory(Sender: TObject); var st: string; stream: TFileStream; buf: PByteArray; i, addr, size: integer; begin openDialog.Filter := 'All Files|*.*'; openDialog.DefaultExt := 'raw'; st := '$08000000'; if openDialog.Execute then if InputQuery('Mappy VM', 'Where should the file be placed in memory?', st) then begin // Figure out the address addr := StrToIntDef(st, 0); // Read in the file stream := TFileStream.Create(openDialog.Filename, fmOpenRead); size := stream.Size; GetMem(buf, size); stream.Read(buf^, size); stream.Free; // Spew the file into memory for i := 0 to size - 1 do vmWriteByte(addr+i, buf^[i]); // Free the temporary buffer FreeMem(buf, size); end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.SaveRawMemory(Sender: TObject); var st: string; stream: TFileStream; buf: PByteArray; i, addr, size: integer; begin saveDialog.Filter := 'All Files|*.*'; saveDialog.DefaultExt := 'raw'; st := '$08000000'; if saveDialog.Execute then if InputQuery('Mappy VM', 'What point in memory should the dump come from?', st) then begin addr := StrToIntDef(st, 0); st := '1024'; if InputQuery('Mappy VM', 'How much should be dumped?', st) then begin // Get memory size := StrToIntDef(st, 10); GetMem(buf, size); // Read the memory for i := 0 to size - 1 do buf^[i] := vmReadByte(addr+i); // Write the file to disk stream := TFileStream.Create(saveDialog.Filename, fmCreate); stream.Write(buf^, size); stream.Free; FreeMem(buf, size); end; end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.ShowMenu(Sender: TObject); var mouse: TPoint; begin GetCursorPos(mouse); popupMenu.Popup(mouse.x, mouse.y); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.ShowHelp(Sender: TObject); begin ShowWebPage(helpFiles.strings[HelpContext-1]); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.ChangeFont(Sender: TObject); begin if fontString <> '' then StringToFont(fontString, fontDialog.Font); if fontDialog.Execute then begin fontString := FontToString(fontDialog.Font); if fontString <> '' then StringToFont(fontString, Font); end; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.FormDestroy(Sender: TObject); begin dbgFindPattern.bFind.OnClick := nil; end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.LoadSettings(ini: TIniFile); begin inherited; fontString := ini.ReadString(OCaption, 'Font', ''); displayMode := THexEditorMode(ini.ReadInteger(OCaption, 'Mode', Ord(hemByte))); end; ////////////////////////////////////////////////////////////////////// procedure TjdevHexEditor.SaveSettings(ini: TIniFile); begin inherited; ini.WriteString(OCaption, 'Font', fontString); ini.WriteInteger(OCaption, 'Mode', Ord(displayMode)); end; ////////////////////////////////////////////////////////////////////// initialization RegisterViewer(TjdevHexEditor); end. //////////////////////////////////////////////////////////////////////