flat assembler
Message board for the users of flat assembler.
Index
> Windows > Writing to an Edit Control Goto page Previous 1, 2, 3, 4 Next |
Author |
|
revolution 06 Aug 2024, 19:39
AsmGuru62 wrote: ...maybe FASM should warn about the case of local/parameter name matching the global name. With regards to the stack. There are some functions of Windows that refuse to use your stack if it isn't in a place that Windows has specifically allocated for use as a stack.. Trying to use your own custom stack in data memory works as far as the CPU is concerned, but problems can occur when Windows sees it isn't in the "right" place. Also the stack points to the bottom, not the top of R0, so it effectively has a stack size of zero size, clobbering whatever has been allocated below R0. |
|||
06 Aug 2024, 19:39 |
|
Stancliff 07 Aug 2024, 00:37
Regarding
1) I saw that CreateWindow for the parent and child returned a handle to the window so I made a var for the parent handle not knowing if it was used. Calling it hwnd was a mistake and it has only beeen used by my MessageBox commands.It will be renamed parhwnd. The child handle has been called edithwnd so it doesn't conflict with the WindowProc parameter and it is referrenced several places. 2) Since the hwnd parameter is needed by the child window and not used outside it to my knowledge, renaming and/or copying it won't be needed. The global can be renamed. 3) I have spent over two hours studying function docs and it is clear that any value in eax when WindowProc returns will go back to DispatchMessage, get used as it's return value, and then be totally ignored by the jump back to msg_loop. They all do nothing and are not needed since eax always has some value. So your change that would have broken wmcreate's .failed sub isn't needed. The thread ends bacause PostQuitMessage puts a WM_QUIT msg with a zero return value as shown in the code. This could be made other values, but isn't in MiniPad. GetMessage takes the WM_QUIT and returns 0 triggering the jump to end_loop. Then ExitProcess returns [msg.wParam] which I am guessing is the return value stated in PostQuitMessage. So that is where any actual return value has to be set... and one is. You are not going to see any of my actual code until I get this editor interface working well posting and returning strings. It could happen within the week though several functions I would like are not written yet... mainly file I/O and search. When I receive a command string it is captured by Query, parsed by Interpret, and compiled by a couple of dozen other functions that I have to port into the working version. Right now they would just get in the way. PushAD is an attempt to build a wall between my code and windows, expecially the OS side. This also applies to my restricting the return stack to the location of the value in [esp] when the program starts. WindowProc can read the prameter frame for it's parameters but my code doesn't use any. All parameters are pushed to a data stack I am pointing to with [edx]. It is completely separate with it's own pushes and pops. This is a critical design feature of Forth and you can look up "starting Forth" by Leo Brodie if you would like details. I will move Cold and the rest of the init back to start to see if I can adjust the stacks before anything has a chance to run. There used to be a fair amount more to do but I cut it way back. That return stack code is making it not create the parent window, but even with those commented out the child isn't working. Here is the modifications I made this evening... Code: ; Simple text editor - fasm example program format PE GUI 4.0 entry start include 'win32ax.inc' macro fPush value ; Push value to TOS (Top Of Stack) { add edx, -4 ; S - 4, new cell for TOS mov [edx], dword value ; move value to TOS } macro Msg label ; msg string typed to screen { fPush label ; Push adr of string to TOS call Type ; Display string on screen } section '.data' data readable writeable ; compute adr's for 2 stacks starting from return stack at enter ; == Return Stack ===== ; R: [ESP] ; Return Stack ptr, in ESP R0 DD 0 ; Ret Stk End, at run = initial [esp] ToR DD 0 ; 1 cell above Top of R, at run =[esp]+64 ; == Data Stack ====== ; S: [EDX] ; Data Stack ptr, in EDX S0: DD 128 Dup ? ; Data Stack End, 128 cells ToS: ; 1 cell above Top of S Stack ; I wanted to use EBP for the Data Stack but I hear Windows uses it ; Note: many good names from Forth are reserved by assembler, find alt's NameSz EQU 256 ; max name cnt, increases dictionary size New^ DD Reserved ; ptr to next unused rec in Name N^ DD 0 ; Name Ptr, index for Name, was DP TIB DB 128 Dup " " ; Term Input Buf adr, 128 b T^ DD 0 ; Text Ptr to current source, was IN Pad DB 128 Dup " " ; Pad adr, 128 b P^ DD Pad ; Pad ptr (temp work area) H^ DD EndKern ; ptr to current unused cell for code Fence DD Reserved -17 ; adr of last protected dictionary word Base DD 10 ; initial value for base conversion ; Start Dictionary with major words of kernal core ; Names have 8 char length w/trailing zeros, no cnt byte for string ; Code Adr = 4 byte, Param Adr = 4 byte, Status = 1 byte (0=IMM,1=Defer) Name: DB "Cold",0,0,0,0 DD Cold,0 DB 1 DB "Abort",0,0,0 DD Abort,0 DB 1 DB "Quit",0,0,0,0 DD Quit,0 DB 1 DB "Query",0,0,0 DD Query,0 DB 1 Reserved: DB ((NameSz *17) +Name -Reserved) Dup ? ; clear Name space ; Forth searches the dictionary to find the address of a command. My bare ; bones kernal will need close to 60 names. The Forth standard has almost ; 140 names, and with extensions like floating point, a simple assembler, ; and various data structures this can easily go over 200. ; Windows assignments _class TCHAR 'Go4th',0 _title TCHAR 'Go4th32',0 _about_title TCHAR 'About Go4th32',0 _about_text TCHAR 'Windows based Forth Compiler',13,10,\ 'Created with FLAT assembler.',0 _edit TCHAR 'EDIT',0 _error TCHAR 'Startup failed.',0 client RECT ; window box sides ClsAtom dd ? editfont dd ? edithwnd dd ? parhwnd dd ? hmenu dd ? msg MSG ; message struct for Windows IDR_ICON = 17 IDR_MENU = 37 ; Menu items IDM_FILE = 100 IDM_NEW = 110 IDM_OPEN = 120 IDM_EXIT = 130 IDM_SVSEL = 140 IDM_SAVE = 150 IDM_SAVEAS = 160 IDM_EDIT = 200 IDM_UNDO = 210 IDM_FIND = 220 IDM_CUT = 230 IDM_COPY = 240 IDM_PASTE = 250 IDM_DEL = 260 IDM_HELP = 900 IDM_ABOUT = 910 wc WNDCLASS 0,WindowProc,0,0,0,0,0,COLOR_BTNFACE+1,0,_class section '.idata' import data readable writeable library advapi32, 'ADVAPI32.DLL',\ comctl32, 'CONCTL32.DLL',\ comdlg32, 'COMDLG32.DLL',\ gdi32, 'GDI32.DLL',\ kernel32, 'KERNEL32.DLL',\ shell32, 'SHELL32.DLL',\ user32, 'USER32.DLL',\ wsock32, 'WSOCK32.DLL' include 'api\advapi32.inc' include 'api\comctl32.inc' include 'api\comdlg32.inc' include 'api\gdi32.inc' include 'api\kernel32.inc' include 'api\shell32.inc' include 'api\user32.inc' include 'api\wsock32.inc' section '.rsrc' resource data readable directory RT_MENU, menus, RT_VERSION, versions, RT_ICON, icons,\ RT_GROUP_ICON, group_icons resource menus, IDR_MENU, LANG_ENGLISH +SUBLANG_DEFAULT, main_menu resource versions, 1, LANG_NEUTRAL, version resource group_icons, IDR_ICON, LANG_NEUTRAL, main_icon resource icons, 1, LANG_NEUTRAL, icon_data versioninfo version, VOS__WINDOWS32, VFT_APP, VFT2_UNKNOWN,\ LANG_ENGLISH +SUBLANG_DEFAULT, 0,\ 'FileDescription', 'MiniPad - example program',\ 'LegalCopyright', 'No rights reserved.',\ 'FileVersion', '1.0',\ 'ProductVersion', '1.0',\ 'OriginalFilename', 'MINIPAD.EXE' icon main_icon, icon_data, 'Go4th.ico' menu main_menu menuitem '&File',IDM_FILE, MFR_POPUP menuitem '&New',IDM_NEW menuitem '&Open',IDM_OPEN menuitem 'E&xit',IDM_EXIT menuseparator menuitem '&SvSelect',IDM_SVSEL menuitem 'Sa&ve',IDM_SAVE menuitem 'Save &As',IDM_SAVEAS, MFR_END menuitem '&Edit',IDM_EDIT, MFR_POPUP menuitem '&Undo',IDM_UNDO menuitem '&Find',IDM_FIND menuseparator menuitem '&Cut',IDM_CUT menuitem 'Co&py',IDM_COPY menuitem '&Paste',IDM_PASTE menuitem '&Delete',IDM_DEL, MFR_END menuitem '&Help',IDM_HELP, MFR_POPUP + MFR_END ; LAST POPUP - MFR_END" menuitem '&About', IDM_ABOUT, MFR_END section '.code' code readable executable writeable start: Push ebx Push ecx Push edx Push esi Push edi ; mov [R0],esp ; set old stack point to new stack start ; mov [ToR],esp ; Top of Return needs to allow 64 cells ; add [ToR],256 ; allow 64 cells for local return stack ; Cold: full restart, reset N^ , New^, and Fence, run Abort Cold: mov eax, [Name] mov [N^], eax mov [New^], eax mov eax, [Fence +17] ; Abort: reset data stack, set base, run Quit Abort: mov edx, S0 ; clear data S wipes all pending params mov [Base], 10 ; Quit: reset return stack. clears all outstanding function calls Quit: ;mov esp, R0 ; clear return R invoke GetModuleHandle,0 mov [wc.hInstance],eax invoke LoadIcon,[wc.hInstance],IDR_ICON mov [wc.hIcon],eax invoke LoadCursor,0,IDC_ARROW mov [wc.hCursor],eax invoke LoadMenu,[wc.hInstance],IDR_MENU mov [hmenu],eax invoke RegisterClass,wc mov [ClsAtom],eax invoke CreateWindowEx,0,_class,_title,WS_TILEDWINDOW+\ WS_VISIBLE,200,100,900,600,0,[hmenu],[wc.hInstance],0 mov [parhwnd],eax msg_loop: invoke GetMessage,msg,0,0,0 cmp eax,1 jb end_loop jge msg_loop invoke TranslateMessage,msg invoke DispatchMessage,msg jmp msg_loop error: invoke MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK end_loop: Pop edi Pop esi Pop edx Pop ecx Pop ebx invoke ExitProcess,[msg.wParam] ; Add most new code here ; Query: Move a null terminated string into Terminal Input Buffer Query: mov [TIB], 123 ; set input limit invoke SendMessage,[edithwnd],EM_GETLINECOUNT,0,0 invoke SendMessage,[edithwnd],EM_GETLINE,eax,dword[TIB] mov [TIB +eax],0 ; add null to string invoke SendMessage,[edithwnd],EM_REPLACESEL,0,NewLine ; Test results - Write input line back out invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,dword[TIB] Banner: DB "Go4th: The Windows Forth Compiler",13,10,0 MyFont: DB "Courier New" NewLine: DB 13,10,0 Prompt: DB "4th?",0 proc WindowProc hwnd,wmsg,wparam,lparam mov eax,[wmsg] cmp eax,WM_CREATE je .wmcreate cmp eax,WM_SIZE je .wmsize cmp eax,WM_SETFOCUS je .wmsetfocus cmp eax,WM_CHAR je .wmchar cmp eax,WM_COMMAND je .wmcommand cmp eax,WM_DESTROY je .wmdestroy .defwndproc: invoke DefWindowProc,[hwnd],[wmsg],[wparam],[lparam] jmp .finish .wmcreate: invoke GetClientRect,[hwnd],client invoke CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+\ WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+\ ES_MULTILINE+ES_WANTRETURN,[client.left],[client.top],\ [client.right],[client.bottom],[hwnd],0,[wc.hInstance],NULL mov [edithwnd],eax cmp eax,0 jmp .finish invoke CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,\ OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,\ FIXED_PITCH+FF_MODERN,MyFont ; invoke MessageBox,[parhwnd],"After Font",0,MB_OK mov [editfont],eax cmp eax,0 jmp .finish invoke SendMessage,[edithwnd],WM_SETFONT,eax,FALSE invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Banner invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Prompt ; Msg Banner ; Msg Prompt jmp .finish .wmsize: invoke GetClientRect,[hwnd],client invoke MoveWindow,[edithwnd],[client.left],[client.top],\ [client.right],[client.bottom],TRUE jmp .finish .wmsetfocus: invoke SetFocus,[edithwnd] jmp .finish .wmchar: invoke SendMessage,[edithwnd],WM_CHAR,ebx,ecx cmp ebx,VK_RETURN jne .defwndproc call Query ; return a null terminated string ; call Interpret ; parse and execute commands jmp .defwndproc .wmcommand: mov eax,[wparam] and eax,0FFFFh cmp eax, IDM_NEW je .new cmp eax, IDM_OPEN je .open cmp eax, IDM_SVSEL je .svSel cmp eax, IDM_SAVE je .save cmp eax, IDM_SAVEAS je .saveAs cmp eax, IDM_UNDO je .undo cmp eax, IDM_FIND je .find cmp eax, IDM_CUT je .cut cmp eax, IDM_COPY je .copy cmp eax, IDM_PASTE je .paste cmp eax, IDM_DEL je .del cmp eax, IDM_ABOUT je .about cmp eax, IDM_EXIT je .wmdestroy jmp .defwndproc .new: invoke SendMessage,[edithwnd],WM_SETTEXT,0,0 jmp .finish .open: ; Load file to Edit Control jmp .finish .svSel:invoke SendMessage,[edithwnd],EM_GETSEL,0,0 ; Write Selected to a File jmp .finish .save: ; Write Session to a File jmp .finish .saveAs: ; Write Session to a different File name jmp .finish .undo: invoke SendMessage,[edithwnd],EM_UNDO,0,0 jmp .finish .find: ; Get Search str and try to Find it in text jmp .finish .cut: invoke SendMessage,[edithwnd],WM_CUT,0,0 jmp .finish .copy: invoke SendMessage,[edithwnd],WM_COPY,0,0 jmp .finish .paste:invoke SendMessage,[edithwnd],WM_PASTE,0,0 jmp .finish .del: invoke SendMessage,[edithwnd],WM_CLEAR,0,0 jmp .finish .about: invoke MessageBox,[parhwnd],_about_text,_about_title,MB_OK jmp .finish .wmdestroy: invoke DeleteObject,[editfont] invoke PostQuitMessage,0 .finish: ret endp EndKern: ; New compiled functions will be added starting here. |
|||
07 Aug 2024, 00:37 |
|
Stancliff 07 Aug 2024, 00:53
revolution wrote:
[/quote] I am fishing for a compromise but am not certain there is one. We agree on this. I will go over that again, stack logic can trick me. R0 and S0 are supposed to be top of an empty stack so resetting them clears the stack data by abandoning all the cells that were there. ToR and ToS need new names. They refer to the first cell address right after the stack data and are used to test for overflow. |
|||
07 Aug 2024, 00:53 |
|
Stancliff 07 Aug 2024, 23:54
My new version today is a bit of a step back to MiniPad. It is creating the child and showing output text, but not reading keys. It is quite possible I set that up wrong.
The docs show an alternative path... "For a multiline edit control in a dialog box, the ES_WANTRETURN style causes the control to insert a carriage return when the user presses the ENTER key while entering text. If this style is not specified, pressing the ENTER key has the same effect as pressing the default push button in the dialog box." It should be enough for me to capture that button event, but I am not certain what to check for. Code: ; Simple text editor - fasm example program format PE GUI 4.0 entry start include 'win32ax.inc' macro fPush value ; Push value to TOS (Top Of Stack) { add edx, -4 ; S - 4, new cell for TOS mov [edx], dword value ; move value to TOS } macro Msg label ; msg string typed to screen { fPush label ; Push adr of string to TOS call Type ; Display string on screen } section '.data' data readable writeable ; compute adr's for 2 stacks starting from return stack at enter ; == Return Stack ===== ; R: [ESP] ; Return Stack ptr, in ESP BoR DD 0 ; 1 cell past end of R, 64 DD =[esp]-256 R0 DD 0 ; Ret Stk start, at run = initial [esp] ; == Data Stack ====== ; S: [EDX] ; Data Stack ptr, in EDX BoS: DD 128 Dup ? ; 1 cell past end of S Stack S0: ; Data Stack start, allow 128 cells ; I wanted to use EBP for the Data Stack but I hear Windows uses it ; Note: many good names from Forth are reserved by assembler, find alt's NameSz EQU 256 ; max name cnt, increases dictionary size New^ DD Reserved ; ptr to next unused rec in Name N^ DD 0 ; Name Ptr, index for Name, was DP TIB DB 128 Dup " " ; Term Input Buf adr, 128 b T^ DD 0 ; Text Ptr to current source, was IN Pad DB 128 Dup " " ; Pad adr, 128 b P^ DD Pad ; Pad ptr (temp work area) H^ DD EndKern ; ptr to current unused cell for code Fence DD Reserved -17 ; adr of last protected dictionary word Base DD 10 ; initial value for base conversion ; Start Dictionary with major words of kernal core ; Names have 8 char length w/trailing zeros, no cnt byte for string ; Code Adr = 4 byte, Param Adr = 4 byte, Status = 1 byte (0=IMM,1=Defer) Name: DB "Cold",0,0,0,0 Cold: DB 1 DB "Abort",0,0,0 Abort: DD Abort,0 DB 1 DB "Quit",0,0,0,0 Quit: DD Quit,0 DB 1 DB "Query",0,0,0 DD Query,0 DB 1 Reserved: DB ((NameSz *17) +Name -Reserved) Dup ? ; clear Name space ; Forth searches the dictionary to find the address of a command. My bare ; bones kernal will need close to 60 names. The Forth standard has almost ; 140 names, and with extensions like floating point, a simple assembler, ; and various data structures this can easily go over 200. IDR_ICON = 17 IDR_MENU = 37 ; Menu entries IDM_FILE = 100 IDM_NEW = 110 IDM_OPEN = 120 IDM_EXIT = 130 IDM_SVSEL = 140 IDM_SAVE = 150 IDM_SAVEAS = 160 IDM_EDIT = 200 IDM_UNDO = 210 IDM_FIND = 220 IDM_CUT = 230 IDM_COPY = 240 IDM_PASTE = 250 IDM_DEL = 260 IDM_HELP = 900 IDM_ABOUT = 910 section '.data' data readable writeable _title TCHAR 'MiniPad',0 _about_title TCHAR 'About MiniPad',0 _about_text TCHAR 'This is Win32 example program created with flat assembler.',0 _error TCHAR 'Startup failed.',0 _class TCHAR 'MINIPAD32',0 _edit TCHAR 'EDIT',0 wc WNDCLASS 0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class edithwnd dd ? editfont dd ? parhwnd dd ? msg MSG client RECT section '.idata' import data readable writeable library kernel,'KERNEL32.DLL',\ user,'USER32.DLL',\ gdi,'GDI32.DLL' import kernel,\ GetModuleHandle,'GetModuleHandleA',\ ExitProcess,'ExitProcess' import user,\ RegisterClass,'RegisterClassA',\ CreateWindowEx,'CreateWindowExA',\ DefWindowProc,'DefWindowProcA',\ SetWindowLong,'SetWindowLongA',\ RedrawWindow,'RedrawWindow',\ GetMessage,'GetMessageA',\ TranslateMessage,'TranslateMessage',\ DispatchMessage,'DispatchMessageA',\ SendMessage,'SendMessageA',\ LoadCursor,'LoadCursorA',\ LoadIcon,'LoadIconA',\ LoadMenu,'LoadMenuA',\ GetClientRect,'GetClientRect',\ MoveWindow,'MoveWindow',\ SetFocus,'SetFocus',\ MessageBox,'MessageBoxA',\ PostQuitMessage,'PostQuitMessage' import gdi,\ CreateFont,'CreateFontA',\ DeleteObject,'DeleteObject' section '.rsrc' resource data readable ; resource directory directory RT_MENU,menus,\ RT_ICON,icons,\ RT_GROUP_ICON,group_icons,\ RT_VERSION,versions ; resource subdirectories resource menus,\ IDR_MENU,LANG_ENGLISH+SUBLANG_DEFAULT,main_menu resource icons,\ 1,LANG_NEUTRAL,icon_data resource group_icons,\ IDR_ICON,LANG_NEUTRAL,main_icon resource versions,\ 1,LANG_NEUTRAL,version menu main_menu menuitem '&File', IDM_FILE, MFR_POPUP menuitem '&New', IDM_NEW menuitem '&Open', IDM_OPEN menuitem '&Exit', IDM_EXIT menuseparator menuitem '&SaveSelect', IDM_SVSEL menuitem 'Sa&ve', IDM_SAVE menuitem 'Save &As', IDM_SAVEAS, MFR_END menuitem '&Edit', IDM_EDIT, MFR_POPUP menuitem '&Undo', IDM_UNDO menuitem '&Find', IDM_FIND menuseparator menuitem '&Cut', IDM_CUT menuitem 'C&opy', IDM_COPY menuitem '&Paste', IDM_PASTE menuitem '&Delete', IDM_DEL, MFR_END menuitem '&Help',IDM_HELP, MFR_POPUP + MFR_END ; LAST POPUP - MFR_END" menuitem '&About', IDM_ABOUT, MFR_END icon main_icon,icon_data,'minipad.ico' versioninfo version,VOS__WINDOWS32,VFT_APP,VFT2_UNKNOWN,LANG_ENGLISH+SUBLANG_DEFAULT,0,\ 'FileDescription','MiniPad - example program',\ 'LegalCopyright','No rights reserved.',\ 'FileVersion','1.0',\ 'ProductVersion','1.0',\ 'OriginalFilename','MINIPAD.EXE' section '.text' code readable executable start: invoke GetModuleHandle,0 mov [wc.hInstance],eax invoke LoadIcon,eax,IDR_ICON mov [wc.hIcon],eax invoke LoadCursor,0,IDC_ARROW mov [wc.hCursor],eax invoke RegisterClass,wc test eax,eax jz error invoke LoadMenu,[wc.hInstance],IDR_MENU invoke CreateWindowEx,0,_class,_title,WS_OVERLAPPEDWINDOW+\ WS_VISIBLE,100,200,900,600,NULL,eax,[wc.hInstance],NULL test eax,eax jz error msg_loop: invoke GetMessage,msg,NULL,0,0 cmp eax,1 jb end_loop jne msg_loop invoke TranslateMessage,msg invoke DispatchMessage,msg jmp msg_loop error: invoke MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK end_loop: ; Pop edi ; Pop esi ; Pop edx ; Pop ecx ; Pop ebx invoke ExitProcess,[msg.wParam] ; Add most new code here ; Query: Move a null terminated string into Terminal Input Buffer Query: mov [TIB], 123 ; set input limit invoke SendMessage,[edithwnd],EM_GETLINECOUNT,0,0 invoke SendMessage,[edithwnd],EM_GETLINE,eax,dword[TIB] mov [TIB +eax],0 ; add null to string invoke SendMessage,[edithwnd],EM_REPLACESEL,0,NewLine ; Test results - Write input line back out invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,dword[TIB] Banner: DB "Go4th: The Windows Forth Compiler",13,10,0 MyFont: DB "Courier New" NewLine: DB 13,10,0 Prompt: DB "4th?",0 proc WindowProc hwnd,wmsg,wparam,lparam push ebx esi edi mov eax,[wmsg] cmp eax,WM_CREATE je .wmcreate cmp eax,WM_SIZE je .wmsize cmp eax,WM_SETFOCUS je .wmsetfocus cmp eax,WM_CHAR je .wmchar cmp eax,WM_COMMAND je .wmcommand cmp eax,WM_DESTROY je .wmdestroy .defwndproc: invoke DefWindowProc,[hwnd],[wmsg],[wparam],[lparam] jmp .finish .wmcreate: invoke GetClientRect,[hwnd],client invoke CreateWindowEx,0,_edit,0,WS_VISIBLE+\ WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+\ ES_MULTILINE+ES_WANTRETURN,[client.left],[client.top],\ [client.right],[client.bottom],[hwnd],0,[wc.hInstance],NULL ; WS_EX_CLIENTEDGE or eax,eax jz .finish mov [edithwnd],eax invoke CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,\ OUT_RASTER_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,\ FIXED_PITCH+FF_DONTCARE,NULL or eax,eax jz .finish mov [editfont],eax invoke SendMessage,[edithwnd],WM_SETFONT,eax,FALSE invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Banner invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Prompt jmp .finish .wmsize: invoke GetClientRect,[hwnd],client invoke MoveWindow,[edithwnd],[client.left],[client.top],\ [client.right],[client.bottom],TRUE jmp .finish .wmsetfocus: invoke SetFocus,[edithwnd] jmp .finish .wmchar: invoke SendMessage,[edithwnd],WM_CHAR,ebx,ecx cmp ebx,VK_RETURN jne .defwndproc invoke MessageBox,[parhwnd],"CR found",0,MB_OK call Query ; return a null terminated string ; call Interpret ; parse and execute commands jmp .defwndproc .wmcommand: mov eax,[wparam] and eax,0FFFFh cmp eax, IDM_NEW je .new cmp eax, IDM_OPEN je .open cmp eax, IDM_SVSEL je .svSel cmp eax, IDM_SAVE je .save cmp eax, IDM_SAVEAS je .saveAs cmp eax, IDM_UNDO je .undo cmp eax, IDM_FIND je .find cmp eax, IDM_CUT je .cut cmp eax, IDM_COPY je .copy cmp eax, IDM_PASTE je .paste cmp eax, IDM_DEL je .del cmp eax, IDM_ABOUT je .about cmp eax, IDM_EXIT je .wmdestroy jmp .defwndproc .new: invoke SendMessage,[edithwnd],WM_SETTEXT,0,0 jmp .finish .open: ; Load file to Edit Control jmp .finish .svSel: ; Write Selected to a File invoke SendMessage,[edithwnd],EM_GETSEL,0,0 jmp .finish .save: ; Write Session to a File jmp .finish .saveAs: ; Write Session to a different File name jmp .finish .undo: invoke SendMessage,[edithwnd],EM_UNDO,0,0 jmp .finish .find: ; Get Search str and try to Find it in text jmp .finish .cut: invoke SendMessage,[edithwnd],WM_CUT,0,0 jmp .finish .copy: invoke SendMessage,[edithwnd],WM_COPY,0,0 jmp .finish .paste:invoke SendMessage,[edithwnd],WM_PASTE,0,0 jmp .finish .del: invoke SendMessage,[edithwnd],WM_CLEAR,0,0 jmp .finish .about: invoke MessageBox,[parhwnd],_about_text,_about_title,MB_OK jmp .finish .wmdestroy: invoke DeleteObject,[editfont] invoke PostQuitMessage,0 .finish: ret endp EndKern: ; New compiled functions will be added starting here. |
|||
07 Aug 2024, 23:54 |
|
AsmGuru62 08 Aug 2024, 21:24
So, I am back from a small delay --- work...
There is an issue with WM_CHAR which I have missed in my post. The WM_CHAR will come to the window, which is focused and that is the child window -- the multi-line edit control. Currently, the case for WM_CHAR is inside the main window procedure, which is not in focus. However, you cannot add this case into the window procedure for an edit control, because this code is inside Windows OS library. You need something called "sub-classing the window" to do this "catching" of <ENTER> key. Another complication. I visited the "Forth" resources and they seem clear. I still think, that it would be much easier to separate your commands from the multi-line log of whatever is going on. Once command is entered -- it can be put into log, so it would look like a conversation of a user with a compiler. As I said, I can make a prototype, and I am not asking for anything, the code I write is free to use, earn money... whatever. Or, and the stack, hmm... I think ESP is better to be left alone. "Forth" has its own stack engine, which can be done without ESP. |
|||
08 Aug 2024, 21:24 |
|
Stancliff 08 Aug 2024, 22:19
Thank you AsmGuru, I appreciate the explanation! I never would have guessed, though some of my attempts today support your info if I had been able to interpreted it right. Even then I wouldn't know what to do.
One more thing to try before I go to the separate input line... did you see my quote from the docs? If you can capture the button press from the edit control when Enter is pressed without ES_WANTRETURN in child definition I don't need to capture the Return at all. Just knowing Return was hit is enough to go ahead and capture the line of text. My current file has been well behaved all day with no significant lockups. The disk access and search functions can wait a little while as long as I can get a command line input. Let me know anything that will help with this. |
|||
08 Aug 2024, 22:19 |
|
Stancliff 08 Aug 2024, 22:51
Quote:
Forth uses two stacks, R and S, and historically R is the ESP return stack. That was a little before Windows. It is barely possible for me to use two registers to set up two stacks for Forth without using ESP, though I really need all the registers at one point or another so that would leave me short by one. This is assuming I can't ever use EBP. My understanding is that Windows uses it as a base address for memory management. This might be an interesting challenge and would keep my Forth stacks from interfering with Windows at all. Down side is it would slow the execution slightly, but not by much. When you have 3.5 billion clocks per second you don't really notice giving up 2% or so. Honestly I am not certain if I can program function calls without the hardware stack. that would force me to micro-manage all functions all the time. I believe that Windows and I won't clash over ESP as long as I don't try to reset the stack after an error. When my code is running, msg_loop will be locked. Only the OS will be running and it is interrupt driven. |
|||
08 Aug 2024, 22:51 |
|
AsmGuru62 09 Aug 2024, 18:52
I think you can use a model of a stack as a structure and leave ESP for your program and Windows to use.
Something of that nature below: Code: ; --------------------------------------------------------------------------- ; FILE: ModelOfStack.Inc ; DATE: August 9, 2024 ; --------------------------------------------------------------------------- ; ; |<----------- ALLOCATE A MEMORY BLOCK ----------->| ; +================+================================+ ; | | DWORD | | ; +================+================================+ ; |<-- BOTTOM |<-- TOP |<-- MAXIMUM ; | ; POP <--|--> PUSH ; --------------------------------------------------------------------------- struct StackModel TopAddress dd ? BottomAddress dd ? MaximumAddress dd ? ends And then some methods (like C++) for STACK management: Code: ; --------------------------------------------------------------------------- ; FILE: ModelOfStack.Asm ; DATE: August 9, 2024 ; --------------------------------------------------------------------------- align 16 proc ModelOfStack_Create uses eax ecx edx esi, stacksize:DWORD ; --------------------------------------------------------------------------- ; Input: ; ebx = pointer to StackModel object ; --------------------------------------------------------------------------- invoke GetProcessHeap mov esi, [stacksize] invoke HeapAlloc, eax, 0, esi mov [ebx + StackModel.BottomAddress], eax mov [ebx + StackModel.TopAddress], eax add eax, esi mov [ebx + StackModel.MaximumAddress], eax ret endp align 16 proc ModelOfStack_Push uses eax edi, value:DWORD ; --------------------------------------------------------------------------- ; Input: ; ebx = pointer to StackModel object ; --------------------------------------------------------------------------- mov edi, [ebx + StackModel.TopAddress] .if (edi < [ebx + StackModel.MaximumAddress]) mov eax, [value] stosd mov [ebx + StackModel.TopAddress], edi .else int3 ; OVERFLOW! .endif ret endp align 16 proc ModelOfStack_Pop uses edi ; --------------------------------------------------------------------------- ; Input: ; ebx = pointer to StackModel object ; Output: ; eax = popped value ; --------------------------------------------------------------------------- mov edi, [ebx + StackModel.TopAddress] .if (edi > [ebx + StackModel.BottomAddress]) sub edi, 4 mov [ebx + StackModel.TopAddress], edi mov eax, [edi] .else int3 ; STACK IS EMPTY! .endif ret endp align 16 proc ModelOfStack_Destroy uses eax ecx edx ; --------------------------------------------------------------------------- ; Input: ; ebx = pointer to StackModel object ; --------------------------------------------------------------------------- invoke GetProcessHeap mov ecx, [ebx + StackModel.BottomAddress] invoke HeapFree, eax, 0, ecx ret endp And that is how this structure is used (ESP is not involved): Code: ; --------------------------------------------------------------------------- ; DATE: August 9, 2024 ; --------------------------------------------------------------------------- format PE GUI 4.0 entry start stack 4000h, 4000h include 'Win32W.Inc' include 'Macros.Inc' include 'Macro\If.Inc' include 'ModelOfStack.Inc' ; {INSMODDEF} Module Definitions inserted immediately before this line ; --------------------------------------------------------------------------- section '.data' data readable writeable Stack1 StackModel Stack2 StackModel ; --------------------------------------------------------------------------- section '.code' code readable executable include 'ModelOfStack.Asm' ; {INSMODIMPL} Module Implementations inserted immediately before this line ; --------------------------------------------------------------------------- ; PROGRAM ENTRY POINT ; --------------------------------------------------------------------------- align 16 start: ; ; Make two stack objects ; mov ebx, Stack1 stdcall ModelOfStack_Create, 0x100000 ; 1Mb for STK1 mov ebx, Stack2 stdcall ModelOfStack_Create, 0x10000 ; 64Kb for STK2 ; ; Now, play around with these objects. ; And, yes, you have to load EBX with proper address, ; but that is how C++ does it also. ; mov esi, Stack2 ; for the loop below mov ebx, Stack1 stdcall ModelOfStack_Push, 'F' stdcall ModelOfStack_Push, 'O' stdcall ModelOfStack_Push, 'R' stdcall ModelOfStack_Push, 'T' stdcall ModelOfStack_Push, 'H' ; ; POP will reverse the text ; mov ecx, 5 .repeat call ModelOfStack_Pop ; ; Check EAX here in debugger: --> H,T,R,O,F ; ; ; Push that EAX result into stack #2 ; push ebx mov ebx, esi ; ESI --> Stack2 stdcall ModelOfStack_Push, eax pop ebx dec ecx .until (ZERO?) ; ; Dispose of both objects ; mov ebx, Stack2 call ModelOfStack_Destroy mov ebx, Stack1 call ModelOfStack_Destroy invoke ExitProcess, 0 ; --------------------------------------------------------------------------- section '.idata' import data readable writeable library kernel32,'KERNEL32.DLL',user32,'USER32.DLL',gdi32,'GDI32.DLL' include 'API\Kernel32.Inc' include 'API\User32.Inc' include 'API\Gdi32.Inc' |
|||
09 Aug 2024, 18:52 |
|
Stancliff 09 Aug 2024, 23:18
I have had the Data stack implemented in my assembly code for a few months. I use it to hold parameters for function calls since that is it's intended purpose. I can't really test without command lines.
You can help me by identifying the response of the edit control that is triggered by 'Enter' when the child is not created with the WANTSRETURN flag. I quoted the online docs two or three posts back and it looks very promising but there is no example and they don't name the event. If that is nearly as hard as capturing the Return key then you can go ahead with your suggestion to put an input line box under the edit control. This isn't what I pictured but it's a very decent idea and I agree it should be simple. It should be positioned to stay at the bottom of the window even when maximized. I saw references to objects in a window being positioned by percents, not pixels. If you can help me get past this issue I can start importing functions and trying to send them data for testing. Everything I wrote needs to be reviewed in relation to the Windows interface. A lot of it will have no impact. My next goal is to import all functions for the minimal kernel one at a time. I need to read up on I/O of files. |
|||
09 Aug 2024, 23:18 |
|
AsmGuru62 10 Aug 2024, 03:35
Find the procedure 'SubClassEditor' --- it does what you need.
Code: ; Simple text editor - fasm example program format PE GUI 4.0 entry start include 'win32ax.inc' macro fPush value ; Push value to TOS (Top Of Stack) { add edx, -4 ; S - 4, new cell for TOS mov [edx], dword value ; move value to TOS } macro Msg label ; msg string typed to screen { fPush label ; Push adr of string to TOS call Type ; Display string on screen } section '.data' data readable writeable ; compute adr's for 2 stacks starting from return stack at enter ; == Return Stack ===== ; R: [ESP] ; Return Stack ptr, in ESP R0 DD 0 ; Ret Stk End, at run = initial [esp] ToR DD 0 ; 1 cell above Top of R, at run =[esp]+64 ; == Data Stack ====== ; S: [EDX] ; Data Stack ptr, in EDX S0: DD 128 Dup ? ; Data Stack End, 128 cells ToS: ; 1 cell above Top of S Stack ; I wanted to use EBP for the Data Stack but I hear Windows uses it ; Note: many good names from Forth are reserved by assembler, find alt's NameSz EQU 256 ; max name cnt, increases dictionary size New^ DD Reserved ; ptr to next unused rec in Name N^ DD 0 ; Name Ptr, index for Name, was DP TIB DB 128 Dup " " ; Term Input Buf adr, 128 b ;T^ DD 0 ; Text Ptr to current source, was IN ;Pad DB 128 Dup " " ; Pad adr, 128 b ;P^ DD Pad ; Pad ptr (temp work area) ;H^ DD EndKern ; ptr to current unused cell for code ;Fence DD Reserved -17 ; adr of last protected dictionary word ; Start Dictionary with major words of kernal core ; Names have 8 char length w/trailing zeros, no cnt byte for string ; Code Adr = 4 byte, Param Adr = 4 byte, Status = 1 byte (0=IMM,1=Defer) Name: DB "Cold",0,0,0,0 DD Cold,0 DB 1 DB "Abort",0,0,0 DD Abort,0 DB 1 DB "Quit",0,0,0,0 DD Quit,0 DB 1 DB "Query",0,0,0 DD Query,0 DB 1 Reserved: DB ((NameSz *17) +Name -Reserved) Dup ? ; clear Name space ; Forth searches the dictionary to find the address of a command. My bare ; bones kernal will need close to 60 names. The Forth standard has almost ; 140 names, and with extensions like floating point, a simple assembler, ; and various data structures this can easily go over 200. ; Windows assignments _class TCHAR 'Go4th',0 _title TCHAR 'Go4th32',0 _about_title TCHAR 'About Go4th32',0 _about_text TCHAR 'Windows based Forth Compiler',13,10,\ 'Created with FLAT assembler.',0 _edit TCHAR 'EDIT',0 _error TCHAR 'Startup failed.',0 _fontface TCHAR 'Courier New',0 client RECT ; window box sides ClsAtom dd ? editfont dd ? edithwnd dd ? hmenu dd ? hwnd dd ? ; <--------- WinOSEditProc dd ? msg MSG ; message struct for Windows IDR_ICON = 17 IDR_MENU = 37 ; Menu items IDM_FILE = 100 IDM_NEW = 110 IDM_OPEN = 120 IDM_EXIT = 130 IDM_SVSEL = 140 IDM_SAVE = 150 IDM_SAVEAS = 160 IDM_EDIT = 200 IDM_UNDO = 210 IDM_FIND = 220 IDM_CUT = 230 IDM_COPY = 240 IDM_PASTE = 250 IDM_DEL = 260 IDM_HELP = 900 IDM_ABOUT = 910 wc WNDCLASS 0,WindowProc,0,0,0,0,0,COLOR_BTNFACE+1,0,_class Banner: DB "Go4th: The Windows Forth Compiler",13,10,0 NewLine: DB 13,10,0 Prompt: DB "4th?",0 section '.idata' import data readable writeable library advapi32, 'ADVAPI32.DLL',\ comctl32, 'CONCTL32.DLL',\ comdlg32, 'COMDLG32.DLL',\ gdi32, 'GDI32.DLL',\ kernel32, 'KERNEL32.DLL',\ shell32, 'SHELL32.DLL',\ user32, 'USER32.DLL',\ wsock32, 'WSOCK32.DLL' include 'api\advapi32.inc' include 'api\comctl32.inc' include 'api\comdlg32.inc' include 'api\gdi32.inc' include 'api\kernel32.inc' include 'api\shell32.inc' include 'api\user32.inc' include 'api\wsock32.inc' section '.rsrc' resource data readable directory RT_MENU, menus, RT_VERSION, versions, RT_ICON, icons,\ RT_GROUP_ICON, group_icons resource menus, IDR_MENU, LANG_ENGLISH +SUBLANG_DEFAULT, main_menu resource versions, 1, LANG_NEUTRAL, version resource group_icons, IDR_ICON, LANG_NEUTRAL, main_icon resource icons, 1, LANG_NEUTRAL, icon_data versioninfo version, VOS__WINDOWS32, VFT_APP, VFT2_UNKNOWN,\ LANG_ENGLISH +SUBLANG_DEFAULT, 0,\ 'FileDescription', 'MiniPad - example program',\ 'LegalCopyright', 'No rights reserved.',\ 'FileVersion', '1.0',\ 'ProductVersion', '1.0',\ 'OriginalFilename', 'MINIPAD.EXE' icon main_icon, icon_data, 'Go4th.ico' menu main_menu menuitem '&File',IDM_FILE, MFR_POPUP menuitem '&New',IDM_NEW menuitem '&Open',IDM_OPEN menuitem 'E&xit',IDM_EXIT menuseparator menuitem '&SvSelect',IDM_SVSEL menuitem 'Sa&ve',IDM_SAVE menuitem 'Save &As',IDM_SAVEAS, MFR_END menuitem '&Edit',IDM_EDIT, MFR_POPUP menuitem '&Undo',IDM_UNDO menuitem '&Find',IDM_FIND menuseparator menuitem '&Cut',IDM_CUT menuitem 'Co&py',IDM_COPY menuitem '&Paste',IDM_PASTE menuitem '&Delete',IDM_DEL, MFR_END menuitem '&Help',IDM_HELP, MFR_POPUP + MFR_END ; LAST POPUP - MFR_END" menuitem '&About', IDM_ABOUT, MFR_END section '.code' code readable executable writeable start: PushAD mov [R0],esp ; set old stack point to new stack start mov [ToR],esp add [ToR],256 ; allow 64 cells for local stack invoke GetModuleHandle,0 mov [wc.hInstance],eax invoke LoadIcon,[wc.hInstance],IDR_ICON mov [wc.hIcon],eax invoke LoadCursor,0,IDC_ARROW mov [wc.hCursor],eax invoke LoadMenu,[wc.hInstance],IDR_MENU mov [hmenu],eax invoke RegisterClass,wc mov [ClsAtom],eax invoke CreateWindowEx,0,_class,_title,WS_TILEDWINDOW+\ WS_VISIBLE,200,100,900,600,0,[hmenu],[wc.hInstance],0 msg_loop: invoke GetMessage,msg,0,0,0 cmp eax,0 je end_loop ; je msg_loop invoke TranslateMessage,msg invoke DispatchMessage,msg jmp msg_loop error: invoke MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK end_loop: invoke ExitProcess,[msg.wParam] PopAD ; Add most new code here ; Cold: full restart, reset bufs, reset N^ and New^, call Abort Cold: mov eax, [Name] mov [N^], eax ; mov eax, [Fence +17] mov [New^], eax ; Abort: reset data stack, set base, print banner, call Quit Abort: mov edx, S0 ; clear data stack S ; mov [Base], 10 ; Quit: set term input, reset return stack, query & interp, print prompt Quit: mov esp, R0 ; Begin: clear return stk R ret ; Query: Move a null terminated string into Terminal Input Buffer Query: mov [TIB], 123 ; set input limit invoke SendMessage,[edithwnd],EM_GETLINECOUNT,0,0 invoke SendMessage,[edithwnd],EM_GETLINE,eax,dword[TIB] mov [TIB +eax],0 ; add null to string invoke SendMessage,[edithwnd],EM_REPLACESEL,0,NewLine ; Test results - Write input line back out invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,dword[TIB] proc WindowProc hwndproc,wmsg,wparam,lparam mov eax,[wmsg] cmp eax,WM_CREATE je .wmcreate cmp eax,WM_SIZE je .wmsize cmp eax,WM_SETFOCUS je .wmsetfocus cmp eax,WM_CHAR je .wmchar cmp eax,WM_COMMAND je .wmcommand cmp eax,WM_DESTROY je .wmdestroy .defwndproc: invoke DefWindowProc,[hwndproc],[wmsg],[wparam],[lparam] ret .wmcreate: mov eax,[hwndproc] mov [hwnd],eax invoke GetClientRect,[hwnd],client invoke CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+\ WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+\ ES_MULTILINE+ES_WANTRETURN,[client.left],[client.top],\ [client.right],[client.bottom],[hwnd],0,[wc.hInstance],NULL mov [edithwnd],eax cmp eax,0 je .failed call SubClassEditor invoke CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,\ OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,\ FIXED_PITCH+FF_MODERN,_fontface mov [editfont],eax cmp eax,0 je error invoke SendMessage,[edithwnd],WM_SETFONT,eax,FALSE invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Banner invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Prompt ; call Cold ; Initialize compiler ; Msg Banner ; Msg Prompt jmp .finish .failed: or eax,-1 ret .wmsize: invoke GetClientRect,[hwnd],client invoke MoveWindow,[edithwnd],[client.left],[client.top],\ [client.right],[client.bottom],TRUE jmp .finish .wmsetfocus: invoke SetFocus,[edithwnd] jmp .finish ; .wmchar: ; invoke SendMessage,[edithwnd],WM_CHAR,ebx,ecx ; cmp ebx,VK_RETURN ; jne .defwndproc ; call Query ; return a null terminated string ; call Interpret ; parse and execute commands ; jmp .defwndproc .wmcommand: mov eax,[wparam] and eax,0FFFFh cmp eax, IDM_NEW je .new cmp eax, IDM_OPEN je .open cmp eax, IDM_SVSEL je .svSel cmp eax, IDM_SAVE je .save cmp eax, IDM_SAVEAS je .saveAs cmp eax, IDM_UNDO je .undo cmp eax, IDM_FIND je .find cmp eax, IDM_CUT je .cut cmp eax, IDM_COPY je .copy cmp eax, IDM_PASTE je .paste cmp eax, IDM_DEL je .del cmp eax, IDM_ABOUT je .about cmp eax, IDM_EXIT je .wmdestroy jmp .defwndproc .new: invoke SendMessage,[edithwnd],WM_SETTEXT,0,0 jmp .finish .open: ; Load file to Edit Control jmp .finish .svSel:invoke SendMessage,[edithwnd],EM_GETSEL,0,0 ; Write Selected to a File jmp .finish .save: ; Write Session to a File jmp .finish .saveAs: ; Write Session to a different File name jmp .finish .undo: invoke SendMessage,[edithwnd],EM_UNDO,0,0 jmp .finish .find: ; Get Search str and try to Find it in text jmp .finish .cut: invoke SendMessage,[edithwnd],WM_CUT,0,0 jmp .finish .copy: invoke SendMessage,[edithwnd],WM_COPY,0,0 jmp .finish .paste:invoke SendMessage,[edithwnd],WM_PASTE,0,0 jmp .finish .del: invoke SendMessage,[edithwnd],WM_CLEAR,0,0 jmp .finish .about: invoke MessageBox,[hwnd],_about_text,_about_title,MB_OK jmp .finish .wmdestroy: invoke DeleteObject,[editfont] PopAD invoke PostQuitMessage,0 .finish: xor eax,eax ret endp proc EditorWndProc uses ebx esi edi, hEditWnd,winmsg,wparam,lparam ; ; All messages for 'EDIT' control now are coming into this function. ; First call the original procedure to do what 'EDIT' does. ; invoke CallWindowProc, [WinOSEditProc], [hEditWnd], [winmsg], [wparam], [lparam] ; ; After 'EDIT' did its job, check if that was WM_CHAR message ; cmp [winmsg], WM_CHAR jne .exit ; ; It was WM_CHAR, now check if it was [ENTER] ; cmp [wparam], VK_RETURN jne .exit ; ; At this point we know that [ENTER] was pressed ; push eax ; Need to preserve EAX. ; It has a return value from original procedure call KeyEnterPressed pop eax .exit: ret endp proc KeyEnterPressed ; ; Use [edithwnd] to get the last line in the control... ; ret endp proc SubClassEditor ; ; Replace the 'EDIT' control procedure with your own to intercept messages ; invoke SetWindowLong, [edithwnd], GWL_WNDPROC, EditorWndProc ; ; Old (Windows 'EDIT') procedure address returns back, so save it. ; It will be used in our 'EditorWndProc' ; mov [WinOSEditProc], eax ret endp |
|||
10 Aug 2024, 03:35 |
|
Stancliff 10 Aug 2024, 17:43
Hmm, read my last message again, I had opted not to sub-class.
|
|||
10 Aug 2024, 17:43 |
|
AsmGuru62 10 Aug 2024, 21:27
I just tried in debugger, the ENTER is not signaled without the sub-classing.
I tried with ES_WANTRETURN and without it. It is possible that if the main window was a dialog, then maybe this trick would have worked. What is even more puzzling is that ES_WANTRETURN does not affect anything in Multi-Line control. Lines are still scrolled up when ENTER is clicked. And if you send text to control with 13,10 --- the lines are separated as specified. Weird, why then invent a style ES_WANTRETURN? |
|||
10 Aug 2024, 21:27 |
|
Stancliff 10 Aug 2024, 21:46
Very odd. Better to go to the separate one line box for command input.
I never got this to run, I may have made one change too many. Code: .wmcreate: mov eax,[hwndproc] mov [parhwnd],eax invoke GetClientRect,[parhwnd],client invoke CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+\ WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+\ ES_MULTILINE+ES_WANTRETURN,[client.left],[client.top],\ [client.right],[client.bottom],[parhwnd],0,[wc.hInstance],NULL mov [edithwnd],eax cmp eax,0 je .failed call SubClassEditor invoke CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,\ OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,\ FIXED_PITCH+FF_MODERN,_myfont mov [editfont],eax cmp eax,0 je error invoke SendMessage,[edithwnd],WM_SETFONT,eax,FALSE invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Banner invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Prompt ; Msg Banner ; Msg Prompt jmp .finish .failed: or eax,-1 ret .wmsize: invoke GetClientRect,[parhwnd],client invoke MoveWindow,[edithwnd],[client.left],[client.top],\ [client.right],[client.bottom],TRUE jmp .finish .wmsetfocus: invoke SetFocus,[edithwnd] jmp .finish .wmcommand: mov eax,[wparam] and eax,0FFFFh cmp eax, IDM_NEW je .new cmp eax, IDM_OPEN je .open cmp eax, IDM_SVSEL je .svSel cmp eax, IDM_SAVE je .save cmp eax, IDM_SAVEAS je .saveAs cmp eax, IDM_UNDO je .undo cmp eax, IDM_FIND je .find cmp eax, IDM_CUT je .cut cmp eax, IDM_COPY je .copy cmp eax, IDM_PASTE je .paste cmp eax, IDM_DEL je .del cmp eax, IDM_ABOUT je .about cmp eax, IDM_EXIT je .wmdestroy jmp .defwndproc .new: invoke SendMessage,[edithwnd],WM_SETTEXT,0,0 jmp .finish .open: ; Load file to Edit Control jmp .finish .svSel:invoke SendMessage,[edithwnd],EM_GETSEL,0,0 ; Write Selected to a File jmp .finish .save: ; Write Session to a File jmp .finish .saveAs: ; Write Session to a different File name jmp .finish .undo: invoke SendMessage,[edithwnd],EM_UNDO,0,0 jmp .finish .find: ; Get Search str and try to Find it in text jmp .finish .cut: invoke SendMessage,[edithwnd],WM_CUT,0,0 jmp .finish .copy: invoke SendMessage,[edithwnd],WM_COPY,0,0 jmp .finish .paste:invoke SendMessage,[edithwnd],WM_PASTE,0,0 jmp .finish .del: invoke SendMessage,[edithwnd],WM_CLEAR,0,0 jmp .finish .about: invoke MessageBox,[parhwnd],_about_text,_about_title,MB_OK jmp .finish .wmdestroy: invoke DeleteObject,[editfont] PopAD invoke PostQuitMessage,0 .finish: xor eax,eax ret endp proc SubClassEditor ; ; Replace the 'EDIT' control procedure with your own to intercept messages ; invoke SetWindowLong, [edithwnd], GWL_WNDPROC, EditorWndProc ; ; Old (Windows 'EDIT') procedure address returns back, so save it. ; It will be used in our 'EditorWndProc' ; mov [WinOSEditProc], eax ret endp proc EditorWndProc uses ebx esi edi, hEditWnd,winmsg,wparam,lparam ; All messages for 'EDIT' control now are coming into this function. ; First call the original procedure to do what 'EDIT' does. invoke CallWindowProc,[WinOSEditProc],[hEditWnd],[winmsg],[wparam],[lparam] ; ; After 'EDIT' did its job, check if that was WM_CHAR message cmp [winmsg], WM_CHAR jne .exit ; ; It was WM_CHAR, now check if it was [ENTER] cmp [wparam], VK_RETURN jne .exit ; ; At this point we know that [ENTER] was pressed push eax ; Need to preserve return value in EAX. ; Use [edithwnd] to get the last line in the control... call Query ; move a terminated string to TIB pop eax .exit: ret endp |
|||
10 Aug 2024, 21:46 |
|
AsmGuru62 11 Aug 2024, 15:27
It seems I found the way to detect the ENTER key.
Please look for "_bEnter" in below code: Code: ; Simple text editor - fasm example program format PE GUI 4.0 entry start include 'win32ax.inc' macro fPush value ; Push value to TOS (Top Of Stack) { add edx, -4 ; S - 4, new cell for TOS mov [edx], dword value ; move value to TOS } macro Msg label ; msg string typed to screen { fPush label ; Push adr of string to TOS call Type ; Display string on screen } section '.data' data readable writeable ; compute adr's for 2 stacks starting from return stack at enter ; == Return Stack ===== ; R: [ESP] ; Return Stack ptr, in ESP R0 DD 0 ; Ret Stk End, at run = initial [esp] ToR DD 0 ; 1 cell above Top of R, at run =[esp]+64 ; == Data Stack ====== ; S: [EDX] ; Data Stack ptr, in EDX S0: DD 128 Dup ? ; Data Stack End, 128 cells ToS: ; 1 cell above Top of S Stack ; I wanted to use EBP for the Data Stack but I hear Windows uses it ; Note: many good names from Forth are reserved by assembler, find alt's NameSz EQU 256 ; max name cnt, increases dictionary size New^ DD Reserved ; ptr to next unused rec in Name N^ DD 0 ; Name Ptr, index for Name, was DP TIB DB 128 Dup " " ; Term Input Buf adr, 128 b ;T^ DD 0 ; Text Ptr to current source, was IN ;Pad DB 128 Dup " " ; Pad adr, 128 b ;P^ DD Pad ; Pad ptr (temp work area) ;H^ DD EndKern ; ptr to current unused cell for code ;Fence DD Reserved -17 ; adr of last protected dictionary word ; Start Dictionary with major words of kernal core ; Names have 8 char length w/trailing zeros, no cnt byte for string ; Code Adr = 4 byte, Param Adr = 4 byte, Status = 1 byte (0=IMM,1=Defer) Name: DB "Cold",0,0,0,0 DD Cold,0 DB 1 DB "Abort",0,0,0 DD Abort,0 DB 1 DB "Quit",0,0,0,0 DD Quit,0 DB 1 DB "Query",0,0,0 DD Query,0 DB 1 Reserved: DB ((NameSz *17) +Name -Reserved) Dup ? ; clear Name space ; Forth searches the dictionary to find the address of a command. My bare ; bones kernal will need close to 60 names. The Forth standard has almost ; 140 names, and with extensions like floating point, a simple assembler, ; and various data structures this can easily go over 200. ; Windows assignments _class TCHAR 'Go4th',0 _title TCHAR 'Go4th32',0 _about_title TCHAR 'About Go4th32',0 _about_text TCHAR 'Windows based Forth Compiler',13,10,\ 'Created with FLAT assembler.',0 _edit TCHAR 'EDIT',0 _error TCHAR 'Startup failed.',0 _fontface TCHAR 'Courier New',0 client RECT ; window box sides ClsAtom dd ? editfont dd ? edithwnd dd ? hmenu dd ? hwnd dd ? ; <--------- WinOSEditProc dd ? msg MSG ; message struct for Windows IDR_ICON = 17 IDR_MENU = 37 ; Menu items IDM_FILE = 100 IDM_NEW = 110 IDM_OPEN = 120 IDM_EXIT = 130 IDM_SVSEL = 140 IDM_SAVE = 150 IDM_SAVEAS = 160 IDM_EDIT = 200 IDM_UNDO = 210 IDM_FIND = 220 IDM_CUT = 230 IDM_COPY = 240 IDM_PASTE = 250 IDM_DEL = 260 IDM_HELP = 900 IDM_ABOUT = 910 ;EN_SETFOCUS IDC_MLEDIT = 1001 ; ID for 'EDIT' control wc WNDCLASS 0,WindowProc,0,0,0,0,0,COLOR_BTNFACE+1,0,_class Banner: DB "Go4th: The Windows Forth Compiler",13,10,0 NewLine: DB 13,10,0 Prompt: DB "4th?",0 _bEnter DB 0 section '.idata' import data readable writeable library advapi32, 'ADVAPI32.DLL',\ comctl32, 'CONCTL32.DLL',\ comdlg32, 'COMDLG32.DLL',\ gdi32, 'GDI32.DLL',\ kernel32, 'KERNEL32.DLL',\ shell32, 'SHELL32.DLL',\ user32, 'USER32.DLL',\ wsock32, 'WSOCK32.DLL' include 'api\advapi32.inc' include 'api\comctl32.inc' include 'api\comdlg32.inc' include 'api\gdi32.inc' include 'api\kernel32.inc' include 'api\shell32.inc' include 'api\user32.inc' include 'api\wsock32.inc' section '.rsrc' resource data readable directory RT_MENU, menus, RT_VERSION, versions, RT_ICON, icons,\ RT_GROUP_ICON, group_icons resource menus, IDR_MENU, LANG_ENGLISH +SUBLANG_DEFAULT, main_menu resource versions, 1, LANG_NEUTRAL, version resource group_icons, IDR_ICON, LANG_NEUTRAL, main_icon resource icons, 1, LANG_NEUTRAL, icon_data versioninfo version, VOS__WINDOWS32, VFT_APP, VFT2_UNKNOWN,\ LANG_ENGLISH +SUBLANG_DEFAULT, 0,\ 'FileDescription', 'MiniPad - example program',\ 'LegalCopyright', 'No rights reserved.',\ 'FileVersion', '1.0',\ 'ProductVersion', '1.0',\ 'OriginalFilename', 'MINIPAD.EXE' icon main_icon, icon_data, 'Go4th.ico' menu main_menu menuitem '&File',IDM_FILE, MFR_POPUP menuitem '&New',IDM_NEW menuitem '&Open',IDM_OPEN menuitem 'E&xit',IDM_EXIT menuseparator menuitem '&SvSelect',IDM_SVSEL menuitem 'Sa&ve',IDM_SAVE menuitem 'Save &As',IDM_SAVEAS, MFR_END menuitem '&Edit',IDM_EDIT, MFR_POPUP menuitem '&Undo',IDM_UNDO menuitem '&Find',IDM_FIND menuseparator menuitem '&Cut',IDM_CUT menuitem 'Co&py',IDM_COPY menuitem '&Paste',IDM_PASTE menuitem '&Delete',IDM_DEL, MFR_END menuitem '&Help',IDM_HELP, MFR_POPUP + MFR_END ; LAST POPUP - MFR_END" menuitem '&About', IDM_ABOUT, MFR_END section '.code' code readable executable writeable start: PushAD mov [R0],esp ; set old stack point to new stack start mov [ToR],esp add [ToR],256 ; allow 64 cells for local stack invoke GetModuleHandle,0 mov [wc.hInstance],eax invoke LoadIcon,[wc.hInstance],IDR_ICON mov [wc.hIcon],eax invoke LoadCursor,0,IDC_ARROW mov [wc.hCursor],eax invoke LoadMenu,[wc.hInstance],IDR_MENU mov [hmenu],eax invoke RegisterClass,wc mov [ClsAtom],eax invoke CreateWindowEx,0,_class,_title,WS_TILEDWINDOW+\ WS_VISIBLE,200,100,900,600,0,[hmenu],[wc.hInstance],0 msg_loop: invoke GetMessage,msg,0,0,0 mov [_bEnter],0 cmp eax,0 je end_loop ; ; Some message received ; mov ecx, msg mov eax, [edithwnd] cmp [ecx + MSG.hwnd], eax jne .translate ; ; We are here if message to be processed by [edithwnd] ; cmp [ecx + MSG.message], WM_CHAR jne .translate ; ; Message was WM_CHAR, last check -- if it was [ENTER] ; cmp [ecx + MSG.wParam], VK_RETURN jne .translate ; ; Here we know that [edithwnd] is getting WM_CHAR for [ENTER] key. ; Simply let it fall through and be processed, but raise a flag. ; inc [_bEnter] .translate: invoke TranslateMessage,msg invoke DispatchMessage,msg ; ; Message is processed, but if flag was raised -- we need to do our own processing ; cmp [_bEnter],0 je msg_loop call EnterKeyWasPressed jmp msg_loop error: invoke MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK end_loop: invoke ExitProcess,[msg.wParam] PopAD ; Add most new code here ; Cold: full restart, reset bufs, reset N^ and New^, call Abort Cold: mov eax, [Name] mov [N^], eax ; mov eax, [Fence +17] mov [New^], eax ; Abort: reset data stack, set base, print banner, call Quit Abort: mov edx, S0 ; clear data stack S ; mov [Base], 10 ; Quit: set term input, reset return stack, query & interp, print prompt Quit: mov esp, R0 ; Begin: clear return stk R ret ; Query: Move a null terminated string into Terminal Input Buffer Query: mov [TIB], 123 ; set input limit invoke SendMessage,[edithwnd],EM_GETLINECOUNT,0,0 invoke SendMessage,[edithwnd],EM_GETLINE,eax,dword[TIB] mov [TIB +eax],0 ; add null to string invoke SendMessage,[edithwnd],EM_REPLACESEL,0,NewLine ; Test results - Write input line back out invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,dword[TIB] proc WindowProc hwndproc,wmsg,wparam,lparam mov eax,[wmsg] cmp eax,WM_CREATE je .wmcreate cmp eax,WM_SIZE je .wmsize cmp eax,WM_SETFOCUS je .wmsetfocus cmp eax,WM_COMMAND je .wmcommand cmp eax,WM_DESTROY je .wmdestroy .defwndproc: invoke DefWindowProc,[hwndproc],[wmsg],[wparam],[lparam] ret .wmcreate: mov eax,[hwndproc] mov [hwnd],eax invoke GetClientRect,[hwnd],client invoke CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+\ WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+\ ES_MULTILINE+ES_WANTRETURN,[client.left],[client.top],\ [client.right],[client.bottom],[hwnd],IDC_MLEDIT,[wc.hInstance],NULL mov [edithwnd],eax cmp eax,0 je .failed invoke CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,\ OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,\ FIXED_PITCH+FF_MODERN,_fontface mov [editfont],eax cmp eax,0 je error invoke SendMessage,[edithwnd],WM_SETFONT,eax,FALSE invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Banner invoke SendMessage,[edithwnd],EM_SETSEL,-1,-1 invoke SendMessage,[edithwnd],EM_REPLACESEL,0,Prompt ; call Cold ; Initialize compiler ; Msg Banner ; Msg Prompt jmp .finish .failed: or eax,-1 ret .wmsize: invoke GetClientRect,[hwnd],client invoke MoveWindow,[edithwnd],[client.left],[client.top],\ [client.right],[client.bottom],TRUE jmp .finish .wmsetfocus: invoke SetFocus,[edithwnd] jmp .finish .wmcommand: mov eax,[wparam] and eax,0FFFFh cmp eax, IDM_NEW je .new cmp eax, IDM_OPEN je .open cmp eax, IDM_SVSEL je .svSel cmp eax, IDM_SAVE je .save cmp eax, IDM_SAVEAS je .saveAs cmp eax, IDM_UNDO je .undo cmp eax, IDM_FIND je .find cmp eax, IDM_CUT je .cut cmp eax, IDM_COPY je .copy cmp eax, IDM_PASTE je .paste cmp eax, IDM_DEL je .del cmp eax, IDM_ABOUT je .about cmp eax, IDM_EXIT je .wmdestroy jmp .defwndproc .new: invoke SendMessage,[edithwnd],WM_SETTEXT,0,0 jmp .finish .open: ; Load file to Edit Control jmp .finish .svSel:invoke SendMessage,[edithwnd],EM_GETSEL,0,0 ; Write Selected to a File jmp .finish .save: ; Write Session to a File jmp .finish .saveAs: ; Write Session to a different File name jmp .finish .undo: invoke SendMessage,[edithwnd],EM_UNDO,0,0 jmp .finish .find: ; Get Search str and try to Find it in text jmp .finish .cut: invoke SendMessage,[edithwnd],WM_CUT,0,0 jmp .finish .copy: invoke SendMessage,[edithwnd],WM_COPY,0,0 jmp .finish .paste:invoke SendMessage,[edithwnd],WM_PASTE,0,0 jmp .finish .del: invoke SendMessage,[edithwnd],WM_CLEAR,0,0 jmp .finish .about: invoke MessageBox,[hwnd],_about_text,_about_title,MB_OK jmp .finish .wmdestroy: invoke DeleteObject,[editfont] PopAD invoke PostQuitMessage,0 .finish: xor eax,eax ret endp proc EnterKeyWasPressed uses esi ebx mov ebx, [edithwnd] invoke SendMessage, ebx, EM_GETLINECOUNT, 0, 0 dec eax ; COUNT-1 --> Last line, dec eax ; but last line is empty, because ENTER makes it so, go back on index once more mov esi, TIB mov word [esi], 128 invoke SendMessage, ebx, EM_GETLINE, eax, esi mov byte [esi + eax], 0 invoke MessageBox, [hwnd], esi, _about_title, MB_OK ret endp |
|||
11 Aug 2024, 15:27 |
|
Stancliff 11 Aug 2024, 21:04
Hey, this makes sense! I had gotten the idea that messages were changed before or after 'Translate". Needless to say grabbing this data here never occurred to me. I know what I will be doing Monday. Many thanks!
I am considering that when my code is running the Windows code is stalled. I can assume my code has a new return stack every time it is entered and then I return it back to the entry state whenever I ret(urn) back to Windows code. The switch each way is trivial and Windows should never see the changes. When my code is finally well tested this should never be an issue, but there is always a chance I mess up my return stack and have to erase it. The degree of reset needed by the system is the basis for the code in Cold, Abort, and Quit. Besides acting as the initialization for the system they can be used to attempt to return back to a previous stable point by adjusting various parameters and clearing one or both stacks. My working version has several changes to the one you are using. Better to change your copy the next time I post one. |
|||
11 Aug 2024, 21:04 |
|
Stancliff 12 Aug 2024, 22:07
Now what are third and fourth lines saying? Shouldn't MSG be msg?
; Some message received mov ecx, msg mov eax, [edithwnd] cmp [ecx + MSG.hwnd], eax jne .translate |
|||
12 Aug 2024, 22:07 |
|
AsmGuru62 13 Aug 2024, 00:43
MSG is a structure defined by FASM:
Code: struct MSG hwnd dd ? message dd ? wParam dd ? lParam dd ? time dd ? pt POINT ends ECX is a pointer to this structure and MSG.hwnd is an offset into the structure (it happens to be 0). The following is basically the same code: Code: mov eax, [edithwnd] cmp eax, [msg.hwnd] I loaded ECX with a pointer, because we need to access a few members of 'msg' on the same code flow. The code is smaller and faster this way. |
|||
13 Aug 2024, 00:43 |
|
Stancliff 13 Aug 2024, 04:15
That was my guess, I remember something like that in the manual... not used to it yet.
|
|||
13 Aug 2024, 04:15 |
|
Stancliff 14 Aug 2024, 17:55
The first time through msg_loop edithwnd is referenced but it hasn't been initialized until after WindowProc runs once for .create to create it's value. Can this create a problem? Should it be initialized in .data?
Code: msg_loop: invoke GetMessage,msg,NULL,0,0 cmp eax,0 je end_loop jb error mov [_bEnter], 0 ; Some message received mov ecx, msg mov eax, [edithwnd] <<==== cmp [ecx + MSG.hwnd], eax jne .translate ; message to be processed by [edithwnd] cmp [ecx + MSG.message], WM_CHAR jne .translate ; Message was WM_CHAR, was it [ENTER]? cmp [ecx + MSG.wParam], VK_RETURN jne .translate ; [edithwnd] is getting WM_CHAR for [ENTER] key. raise a flag. inc [_bEnter] Last edited by Stancliff on 14 Aug 2024, 19:30; edited 1 time in total |
|||
14 Aug 2024, 17:55 |
|
Goto page Previous 1, 2, 3, 4 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.