;(c) copyright Barry Kauler 1997
;Companion Disk, "Windows Assembly Language & Systems programming".
;W32DEMO.ASM --> W32DEMO.EXE	Windows 95 demo program.
;This skeleton assembly language program has been written for TASM5.0.
;It has the startup code built-in, rather than as a separate object file.

;THIS PROGRAM IS WRITTEN TO WORK WITH A 16-BIT WINAPP, AND HAVE
;ACCESS TO ITS DATA SEGMENT.
;Barry Kauler's program GOOFEE.EXE (bundled with "Flow Design for
;Embedded Systems") is an example. It is a 16-bit Winapp, that copies
;almost the entire data segment (0--F000) to below 1M, then writes the data
;selector to the file GLOBALDS.DAT. When GOOFEE.EXE terminates, it
;zeroises the word value in GLOBALDS.DAT.
;Explanation how GOOFEE.EXE does this is given in the "Windows Assembly
;Language & Systems Programming" book.
;http://www.arrowweb.com/goofee/

;***TO USE***
;Start GOOFEE.EXE first, then W32DEMO.EXE, arrange so both windows show
;side-by-side. Draw a simple diagram, with node 1 having at least one
;wire attached. With W32DEMO window active, press any key. The demo then
;puts GOOFEE through these steps:
;1. Simulate a right mouse button on node 1
;2. Move node 1 to the right (plus anything attached)
;3. Zoom out
;4. Pan right, by a small amount
;5. Save the file to disk   --- 4-97, bypassed
;6. Modify color of node 1
;Pressing any key again -- after focus returns to the window of this app--
;will repeat the demo.

;step 6 above is a "stress test", as it puts this app in a tight loop
;writing a sequence of colors to node 1. To evaluate multitasking, you
;might like to work on the diagram in the GOOFEE window while W32DEMO
;is simultaneously changing node color. Try zooming in, to see node 1
;enlarged ....

.386
.MODEL FLAT,STDCALL
UNICODE = 0
INCLUDE W32.INC

IDM_QUIT		EQU	100		;menu-identifiers -- must be
IDM_ABOUT		EQU	101		;same as defined in .RC file.


.DATA
;--------------------------------------------------------------------------

hInst		 DD 0
mainhwnd	 DD 0
s1		 WNDCLASS    <?>
s2		 MSG	 <?>
s3	 PAINTSTRUCT <?>
szTitleName	 DB "Win32 Assembly Language Demo Program",0
szClassName	 DB "W32DEMO",0
sziconname	 DB "ICON_1",0	    ;name of icon in .RC file.

g_hwnd	  DWORD 0
g_message   DWORD 0
g_wparam    DWORD 0
g_lparam    DWORD 0

szaboutstr  DB "This is an about-box",0 ;messagebox
sztitlestr  DB "Barry Kauler 1997",0	;/

hglobalds   DD 0		;handle for GLOBALDS.DAT
szglobalds  DB "GLOBALDS.DAT",0
globalds    DW 0		;contents read from GLOBALDS.DAT
szerr	    DB "ERROR",0
szgerr1     DB "Could not open GLOBALDS.DAT. Terminating.",0
szgerr2     DB "No selector in GLOBALDS.DAT. Terminating.",0
bytesread   DD 0		;number of bytes read from file.
szokay	    DB "OKAY",0
szinstruct  DB "When this window has keyboard focus, press any key...",0
thisfocus   DD 0
pnode1	    DW 0    ;ptr to node 1.


.CODE
;-----------------------------------------------------------------------------
start:

	call GetModuleHandle, NULL
	mov	hInst, eax

; initialize the WndClass structure
	mov	s1.w_style, CS_HREDRAW + CS_VREDRAW + CS_DBLCLKS
	mov	s1.w_lpfnWndProc, offset ASMWNDPROC
	mov	s1.w_cbClsExtra, 0
	mov	s1.w_cbWndExtra, 0

	mov	eax, hInst
	mov	s1.w_hInstance, eax

;  call LoadIcon, NULL, IDI_APPLICATION	  ;loads default icon.
;let's load a custom icon....
	call LoadIcon, hInst, OFFSET sziconname
	mov	s1.w_hIcon, eax

  call LoadCursor,NULL, IDC_ARROW
	mov	s1.w_hCursor, eax

	mov	s1.w_hbrBackground, COLOR_WINDOW + 1
	mov	s1.w_lpszMenuName, OFFSET szClassName
	mov	s1.w_lpszClassName, OFFSET szClassName

  call RegisterClass, OFFSET s1

    call CreateWindowEx,0,OFFSET szClassName,OFFSET szTitleName, \
	WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT, \
	CW_USEDEFAULT,CW_USEDEFAULT, 0, 0, hInst, 0
	mov	mainhwnd, eax

    call ShowWindow, mainhwnd,SW_SHOWNORMAL
    call UpdateWindow, mainhwnd

;4-97
;To directly access data segment of another application, it has exported
;a selector in file GLOBALDS.DAT...
;    call _lOpen, OFFSET szglobalds,OF_READ
    call CreateFile,OFFSET szglobalds,GENERIC_READ,0,0,OPEN_EXISTING,\
					FILE_ATTRIBUTE_NORMAL,0
    cmp  eax,INVALID_HANDLE_VALUE
    je	 gerr1
    mov  hglobalds,eax
;    call _lRead, hglobalds,OFFSET globalds,2	  ;read 2 bytes, to globalds
    call ReadFile, hglobalds,OFFSET globalds,2,OFFSET bytesread,0
;    call _lClose, hglobalds
    call CloseHandle, hglobalds
    cmp  globalds,0		;does file have a valid selector?
    je	 gerr2
    jmp  SHORT msg_loop
gerr1:
 call MessageBox,mainhwnd,OFFSET szgerr1,OFFSET szerr,MB_OK+MB_ICONEXCLAMATION
    jmp end_loop
gerr2:
 call MessageBox,mainhwnd,OFFSET szgerr2,OFFSET szerr,MB_OK+MB_ICONEXCLAMATION
    jmp  end_loop

msg_loop:
    call GetMessage, OFFSET s2, 0,0,0
	cmp	ax, 0
        je      end_loop
    call TranslateMessage, OFFSET s2
    call DispatchMessage, OFFSET s2
	jmp	msg_loop

end_loop:
    call ExitProcess, s2.ms_wParam

;-----------------------------------------------------------------------------
    PUBLIC ASMWNDPROC
ASMWNDPROC proc STDCALL, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam:DWORD
	USES  ebx, edi, esi
	LOCAL	hDC:DWORD

    mov eax,hwnd	    ;useful to make these static.
    mov g_hwnd,eax
    mov eax,wmsg
    mov g_message,eax
    mov eax,wparam
    mov g_wparam,eax
    mov eax,lparam
    mov g_lparam,eax


	xor eax,eax
	mov ax,WORD PTR g_message
	.IF ax==WM_DESTROY
	  call	wmdestroy
	.ELSEIF ax==WM_RBUTTONDOWN
	  call	wmrbuttondown
	.ELSEIF ax==WM_SIZE
	  call	wmsize
	.ELSEIF ax==WM_CREATE
	  call	wmcreate
	.ELSEIF ax==WM_LBUTTONDOWN
	  call	wmlbuttondown
	.ELSEIF ax==WM_PAINT
	  call	wmpaint
	.ELSEIF ax==WM_COMMAND
	  call	wmcommand
	.ELSEIF ax==WM_CHAR
	  call	wmchar
	.ELSE
	  call DefWindowProc, hwnd,wmsg,wparam,lparam
	  ret
	.ENDIF
	xor eax,eax
	ret
ASMWNDPROC ENDP
;-----------------------------------------------------------------------------
wmcommand PROC
	mov ax,WORD PTR g_lparam
	.IF ax==0
	  mov  ax,WORD PTR g_wparam
	  .IF ax==IDM_QUIT
	    call PostQuitMessage,0
	  .ELSEIF ax==IDM_ABOUT
	   call MessageBox, g_hwnd, OFFSET szaboutstr, OFFSET sztitlestr, MB_OK
	  .ENDIF
	.ENDIF
    ret
wmcommand ENDP
;------------------------------------------------------------------------
wmpaint PROC
    call BeginPaint, hwnd,OFFSET s3
	mov	hDC, eax

;One thing to be very careful about, is Windows can renter ASMWNDPROC
;-- for example, calling UPDATEWINDOW() will immediately execute
;ASMWNDPROC, with WM_PAINT -- even if execution is currently inside
;ASMWNDPROC!  Therefore, make sure WM_PAINT is reentrant.
;another one to watch is WM_DESTROY, that is generated if user
;selects System-menu/Close.
;The situation in this program is that UPDATEWINDOW() is in WINMAIN()
;and ASMWNDPROC is called immediately, *not* when we get down to the
;message-loop -- i.e., the message queue is bypassed.
;Therefore, this procedure, wmpaint, gets entered before globalds
;has a valid selector, because of the order in which I wrote the
;code in WINMAIN() -- so, check for zero.
;... however, to generate another WM_PAINT message, to reenter here
;after globalds has a valid selector, you need to do something to the
;window, like resize it by dragging a side.

;globalds has a selector to data segment of GOOFEE.EXE. this is only valid
;if GOOFEE.EXE was started before this application, and is still running.
  .IF globalds==0	    ;see above note.
  .ELSE
    mov  es,globalds
;the info in the data segment of GOOFEE.EXE is published on-line, at:
;http://www.arrowweb.com/goofee/
;offset 00+32 has the value 01... let's find out...
    mov  si,32
    xor  eax,eax
    mov  al,es:[si+00]	 ;NOTE: could use equate "xversion EQU 00"
    .IF al==01
    call TextOut, hDC,20,20,OFFSET szokay,4
    call TextOut, hDC,20,40,OFFSET szinstruct,53
    .ENDIF

;right, we have access to GOOFEE's data segment (you should see "OKAY"
;on the screen).

  .ENDIF

    call EndPaint, hwnd,OFFSET s3
    ret
wmpaint ENDP
;........................................................................
wmchar	PROC
;let's send a command to GOOFEE upon receipt of any keypress...

;as some steps below will require keyboard input to the GOOFEE program,
;first make GOOFEE the active window...
  mov  es,globalds
  mov  si,32
  mov  eax,es:[si+63]	;get hwindow1
  call SetForegroundWindow, eax

;A value of 80h in the control1 field (at offset 48+32)
;means GOOFEE is ready for a command, that you write to bits 0--6
;of control1...
;NOTE: one thing to be careful about, is API functions destroy ES.
    mov  es,globalds
    mov  si,32	      ;offset of start of database.
wmc3:
    mov  al,es:[si+48]	;note you could use an equate "xcontrol1 EQU 48"
    cmp  al,80h
    jne  wmc3

      ;ok, assuming that there **already exists** node 1 in the window
      ;of GOOFEE Diagrammer...
      ;let's simulate a right-mouse-button press...
      mov  es:[si+49],WORD PTR 01	;number of object (onumber field)
      mov  es:[si+48],BYTE PTR 10100001b ;bit5=simulate right button
				    ;bits0,1=01 node array.
      ;that's it! GOOFEE will clear bits0--6 when done, and will set
      ;bit7 when ready for another command (i.e., wait for 80h).

;further demo, move node 1 on the screen...
wmc4:
    mov  al,es:[si+48]	;note you could use an equate "xcontrol1 EQU 48"
    cmp  al,80h
    jne  wmc4
;first, white-out its current position (which will automatically
;white-out all attached wires and sub-nodes)...
      mov  es:[si+49],WORD PTR 01	;number of object (onumber field)
      mov  es:[si+48],BYTE PTR 10010001b ;bit4=1 white-out object.
				    ;bits0,1=01 node array.

;now we will directly write to an object in the database...
wmc5:
    mov  al,es:[si+48]	;note you could use an equate "xcontrol1 EQU 48"
    cmp  al,80h
    jne  wmc5

	mov  ax,1	;number of object(must exist in GOOFEE window).
	dec  ax
	mov  bx,es:[si+1]	;could use "xsizeobject EQU 1".
	mul  bx
	add  ax,es:[si+13] ;"xpnodeobjects EQU 13".
	add  ax,1024	;"xobjects EQU 1024".
	;...now have absolute offset of object (offset from start of data
	;segment) let's move the object right by 30 ...
	mov  bx,ax
	mov  pnode1,ax	    ;save it for later.
	mov  ax,es:[bx+9]  ;"xxtopleft EQU 9".
	add  ax,30
	mov  es:[bx+9],ax
	mov  ax,es:[bx+42] ;"xxmid EQU 42".
	add  ax,30
	mov  es:[bx+42],ax

;the above has moved it in the database, now tell GOOFEE to redraw it...
;(GOOFEE will *automatically* redraw all attached wires & sub-nodes!)
    mov  es:[si+49],WORD PTR 01 	;node number 1.
    mov  es:[si+48],BYTE PTR 10000101b	;bit2=1 redraw, bits1,0=01 node.

;continuing the demo, let's control zoom factor...
wmc6:
    mov  al,es:[si+48]	;note you could use an equate "xcontrol1 EQU 48"
    cmp  al,80h
    jne  wmc6
    mov  al,es:[si+62]	;"xzfactor EQU 62"
    .IF  al==0
      mov es:[si+62],BYTE PTR 1 ;zoom out
    .ELSEIF al==1
      mov es:[si+62],BYTE PTR 2 ;zoom out again
    .ELSEIF al==2
      mov es:[si+62],BYTE PTR 0 ;back to normal size.
    .ELSE
      mov es:[si+62],BYTE PTR 0
    .ENDIF

;to get this new zoom-factor to show, must redraw window...
     mov  es:[si+52],BYTE PTR 10000000b	;bit7=1 redraw entire diagram.
				;"xcontrol3 EQU 52"
     mov  es:[si+48],BYTE PTR 11000000b ;bit6=1 extended control2/3.
     ;NOTE that I've set this last, as control1 is what GOOFEE
     ;monitors, waiting for any bits2--6 to be set.

;pan to the right, by a small amount...
     mov  es:[si+51],BYTE PTR 01000000b	;bit6=1 pan right.
				;could use "xcontrol2 EQU 51"
     mov  es:[si+48],BYTE PTR 11000000b ;bit6=1 extended control2/3.


jmp bypass1	     ;leave it out for now.
;now, save the file...
wmc8:
    mov  al,es:[si+48]	;note you could use an equate "xcontrol1 EQU 48"
    cmp  al,80h
    jne  wmc8
    mov  bx,es:[si+67]	;could have "xpszfile EQU 67"
	;pszfile is a pointer to szfile, a 255 character path/filename.
	;note that whenever a data file is opened, szfile will have
	;this info.
    mov  es:[bx],BYTE PTR "D"	  ;very crude way to copy a string!!!
    mov  es:[bx+1],BYTE PTR "."   ;/ could put entire path and filename,
    mov  es:[bx+2],BYTE PTR "G"   ;/ up to 255 characters.
    mov  es:[bx+3],BYTE PTR "O"   ;/ this is just D.GOO
    mov  es:[bx+4],BYTE PTR "O"   ;/
    mov  es:[bx+5],BYTE PTR 0	  ;/ null termination.
    mov  es:[si+52],BYTE PTR 01000000b	;bit6=1 save file to disk.
			    ;could use "control3 EQU 52"
    mov  es:[si+48],BYTE PTR 11000000b	;bit6=1 extended control2/3.

bypass1:
;ok, change fill-color of node 1 ....
    mov  bx,pnode1	    ;address of node 1, calc above
    or	 es:[bx+5],BYTE PTR 0100b	;bit2=1 turn on color.
    mov  ecx,0FFFFFFFFh
L1:
wmc9:
    mov  al,es:[si+48]	;control1
    cmp  al,80h
    jne  wmc9
    mov  es:[bx+32],ecx     ;write to .color field of node 1.
    mov  es:[si+49],WORD PTR 01 	;node number 1.
    mov  es:[si+48],BYTE PTR 10000101b	;bit2=1 redraw, bits1,0=01 node.
    sub  ecx,10000000 ;speed up
    jnc  L1	 ;fall out when reach zero.



;when GOOFEE has finished processing last command, change focus back
;to window of this app...
wmc7:
    mov  al,es:[si+48]	;note you could use an equate "xcontrol1 EQU 48"
    cmp  al,80h
    jne  wmc7
    call SetForegroundWindow, hwnd


    ret
wmchar	ENDP
;........................................................................

wmcreate PROC
    ret
wmcreate ENDP

wmdestroy PROC
    call PostQuitMessage,0
    ret
wmdestroy ENDP

wmlbuttondown PROC
    ret
wmlbuttondown ENDP

wmrbuttondown PROC
    call MessageBeep,0
    ret
wmrbuttondown ENDP

wmsize PROC
    ret
wmsize ENDP

;-----------------------------------------------------------------------------
ends
end start

