*
* Marinetti / SMB1 Test (THIS IS NOT SMB2), Mark II
*
* CHANGELOG
* Thursday, April 30, 2015 - Restarted effort, ported code from ORCA/M to Merlin32 and debugged Merlin32 environment
* Friday, May 1, 2015      - Ported more code to Merlin32, set up Merlin32 equates
* Saturday, May 2, 2015    - Formatting fixes, refactoring, rewritten SMB Negotiation code
* Saturday, May 9, 2015    - Receive and interpret NEG_PROT reply and start login
* Sunday, May 24, 2015     - Some bugfixes, Tool128 and Tool129 requirement for hashing and DES, LM password hashing support
*                            Also introducing successful SMB_Tree_ANDX message. We connect to remote shares now.
*                            Also introducing SMB_Open_ANDX message. We open a file now.
* Monday, May 25, 2015     - Bugfixes on SMB_Open_ANDX; file opening now successful
*                            Also introducing SMB_Read_ANDX message. We read a file now.
*
* REFERENCES
* smb.c / smb.h from libOGC
* http://www.ubiqx.org/cifs/SMB.html
*

				rel
				dsk smbdemo.l

* Macros (mostly from Merlin32 dist)
				use GsOs.Macs
				use Util.Macs
				use Misc.Macs
				use Locator.Macs
				use Mem.Macs
				use Qd.Macs
				use QdAux.Macs
				use Event.Macs
				use Window.Macs
				use Ctl.Macs
				use Menu.Macs
				use Desk.Macs
				use Sound.Macs
				use Line.Macs
				use Dialog.Macs
				use TCPIP.Macs		; from MOSP CVS

* Equates (ripped off from Merlin 16)		
				use E16.Dialog.Macs
				use E16.GsOs.Macs
* Equates (from MOSP/Tools/Orca.M/e16.tcpip)
				use E16.TCPIP.Macs

*-----------------------------------------------------------------------------
* DP variables and local equates
*-----------------------------------------------------------------------------

* DUM $000000 is broken on merlin32, if you want LDA (DP) $A5 opcode, write one-byte equates instead
* cause merlin32 will always give you $AD (LDAL)
*		dum 0
*AppMMID
*		ds 2,0				; Memory Manager ID
*HndlRef
*		ds 2,0				; For Dereferencing MM Handle
*DPBase
*		ds 1,0				; Deref'd handle
*		dend

* workaround: do this instead for DP variables
AppMMID			= $0
HndlRef			= $2
DPBase			= $4

*-----------------------------------------------------------------------------
* GS/OS Program Entry Point
*-----------------------------------------------------------------------------

Entry			mx %00
				phk
				plb
				brl Main

*
* Check for toolbox errors
* too-simple error handler
*
CheckError		bcs SysDeath
				rts

SysDeath		pha
				PushLong #0000
				_SysFailMgr

*
* StartUpTools - start up the various toolsets
*
StartUpTools 	_TLStartUp
				pha
				_MMStartUp
				jsr CheckError
				PullWord AppMMID
				_MTStartUp

				pha
				pha
				PushLong #$900		; 9 pages of DP space
				PushWord AppMMID
				PushWord #$C005		; Fixed, Page-Aligned, Locked, Unpurgeable
				PushLong #$000000	; in Bank $00, $0000
				_NewHandle
				jsr CheckError
				PullLong HndlRef
				lda [HndlRef]
				sta DPBase

				pha					; save dp pointer for later
				pha
				PushWord #$0080		; Screen Mode ($0000 = 320, $0080 = 640)
				PushWord #$00A0		; Pixel Map Size ($0050 = 320, $00A0 = 640)
				PushWord AppMMID
				_QDStartUp
				jsr CheckError

				pla					; saved dp pointer
				clc
				adc #$300
				pha					; save dp pointer for later
				pha
				PushWord #20		; Event Queue size
				PushWord #0			; Min X clamp
				PushWord #640		; Max X clamp
				PushWord #0			; Min Y clamp
				PushWord #200		; Max Y clamp
				PushWord AppMMID
				_EMStartUp
				jsr CheckError

				PushWord #0
				_SetBackColor

				PushWord #3
				_SetForeColor

				PushWord #50		; X position
				PushWord #85		; Y position
				_MoveTo

				PushLong #OneMoment
				_DrawCString

				PushLong #ToolList
				_LoadTools
				jsr CheckError

				PushWord AppMMID
				_WindStartUp
				jsr CheckError

				pla					; saved dp pointer
				clc
				adc #$100
				pha					; save it for later
				tax					; save dp pointer in x
				PushWord AppMMID
				phx
				_CtlStartUp
				jsr CheckError

				pla
				clc
				adc #$100
				pha
				tax
				PushWord AppMMID
				phx
				_LEStartUp
				jsr CheckError
		
				PushWord AppMMID
				_DialogStartUp
				jsr CheckError

				pla
				clc
				adc #$100
				pha
				tax
				PushWord AppMMID
				phx
				_MenuStartUp
				jsr CheckError

				_DeskStartUp
				jsr CheckError
		
				pla
				clc
				adc #$100
				pha
				pha
				_SoundStartUp
				jsr CheckError

				_TCPIPStartUp
				jsr CheckError
				
				Tool $280			; HashStartUp
				jsr CheckError
				
				pla
				clc
				adc #$100
				pha
				Tool $281			; CryptoStartUp

				rts

*
* ShutDownTools - shut em back down
*
ShutDownTools	lda ToolList
				asl
				asl
				tax
				lda ToolList-2,x
				cmp #$2				; Memory Manager?
				bne ShutDownOne		; Nope, do it normally
				PushWord AppMMID
				_DisposeAll
				PushWord AppMMID
				lda #$2				; set up for toolset #2 (MM)
ShutDownOne		ora #$300			; Call #3 (Shutdown)
				tax
				jsl $E10000
				dec ToolList
				bne ShutDownTools
				rts

*
* Main - main sequence of events
*
Main			jsr StartUpTools
				jsr PrepDeskTop
				sep $20
				ldal $e0c034
				sta ScreenColorByte
				lda #00
				stal $e0c034
				rep $30

* pretty corner rounding
				PushWord #0000
				_SetForeColor
* upper left
				PushWord #0001
				PushWord #0000
				_MoveTo
				PushWord #0001
				PushWord #0002
				_LineTo
				PushWord #0002
				PushWord #0000
				_MoveTo
				PushWord #0002
				PushWord #0001
				_LineTo
				PushWord #0003
				PushWord #0000
				_MoveTo
				PushWord #0003
				PushWord #0000
				_LineTo
* upper right
				PushWord #0638
				PushWord #0000
				_MoveTo
				PushWord #0638
				PushWord #0002
				_LineTo
				PushWord #0637
				PushWord #0000
				_MoveTo
				PushWord #0637
				PushWord #0001
				_LineTo
* lower left
				PushWord #0000
				PushWord #0195
				_MoveTo
				PushWord #0000
				PushWord #0199
				_LineTo
				PushWord #0001
				PushWord #0197
				_MoveTo
				PushWord #0001
				PushWord #0199
				_LineTo
				PushWord #0002
				PushWord #0199
				_MoveTo
				PushWord #0002
				PushWord #0199
				_LineTo
* lower right
				PushWord #0639
				PushWord #0195
				_MoveTo
				PushWord #0639
				PushWord #0199
				_LineTo
				PushWord #0638
				PushWord #0197
				_MoveTo
				PushWord #0638
				PushWord #0199
				_LineTo
				PushWord #0637
				PushWord #0199
				_MoveTo
				PushWord #0637
				PushWord #0199
				_LineTo

EventLoop		pha					; Result Space
				PushWord #$ffff		; Event Mask
				PushLong #EventRec
				_TaskMaster
				pla
				beq EventLoop

				cmp #$11			; wInMenuBar?
				bne EventLoop		; no, so ignore

				jsr MenuDispatch
				bit QuitFlag
				bpl EventLoop

				sep $20
				lda ScreenColorByte
				stal $e0c034
				rep $30

				jsr ShutDownTools
				iGSOS _Quit;QParams;1

*
* Prepare Desktop
*
PrepDeskTop		PushLong #0000
				_RefreshDesktop
				_InitCursor

NextMenu		pha
				pha
				lda MenuTbl
				asl
				tax
				lda MenuTbl,x
				phb
				phb
				pha
				_NewMenu
				PushWord #0000
				_InsertMenu

				dec MenuTbl
				bne NextMenu

				PushWord #1
				_FixAppleMenu

				pha
				_FixMenuBar
				pla

				_DrawMenuBar
				rts

*
* Apple Menu: About
*
About			PushLong #AboutString
				jsr InfoDialog
				rts

*
* File Menu: Quit
*
Quit			dec QuitFlag
				rts

*
* TCP/IP Test
*
TCPIPTest		pha
				PushWord #0000
				_TCPIPGetConnectStatus
				pla
				cmp #$0000
				bne tgood
				PushLong #MarinettiBad
				jsr InfoDialog
				bra tret
tgood			PushLong #MarinettiGood
				jsr InfoDialog
tret			pla
				rts

*
* Connect To Server
*
ConnectToSvr	PushLong #$00000000		; space for result
				PushLong #CTSRect
				_GetNewModalDialog
				PullLong CTSWinPtr

				PushLong CTSWinPtr
				_BeginUpdate

				PushLong CTSWinPtr
				_DrawDialog

				PushLong CTSWinPtr
				_EndUpdate

				PushLong #00000000
				_TCPIPConnect			; make sure marinetti is connected

				PushLong #00000000
				_TCPIPGetMyIPAddress
				plx
				ply
				PushWord #0000
				phy
				phx
				PushLong #ascaddr
				PushWord #0000
				_TCPIPConvertIPToASCII	; convert our address to ASCII
				pla
				PushLong CTSWinPtr
				PushWord #1360
				PushLong #ascaddr
				_SetIText				; show it

CTSLoop1		PushWord #0000			; space for result
				PushLong #00000000		; nil filter
				_ModalDialog
				pla						; get item hit
				cmp #2					; Cancel?
				beq CTSClose
				cmp #1					; Connect?
				bne CTSLoop1
				bra CTSConnect
CTSClose		PushLong CTSWinPtr
				_CloseDialog
				rts
CTSConnect		PushLong #00000000
				PushLong CTSWinPtr
				PushWord #0001
				_GetControlDItem
				plx
				ply
				PushWord #255
				phy
				phx
				_HiliteControl			; dim/disable 'Connect' button
		
				PushLong #00000000
				PushLong CTSWinPtr
				PushWord #1349
				_GetControlDItem
				plx
				ply
				PushWord #255
				phy
				phx
				_HiliteControl			; dim/disable 'Address' text input

				PushLong CTSWinPtr
				PushWord #1349
				PushLong #fqdnptr
				_GetIText
		
				PushWord CTSText4		; preserve old length byte (word)
		
				sep #$30
				mx %11
				lda fqdnptr
				tax
				clc
				adc CTSText4
				sta CTSText4
hostcopy		lda fqdnptr,x
				sta inhost-1,x
				dex
				bne hostcopy
				rep #$30
				mx %00
		
				PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSText4
				_SetIText				; show 'resolving' status string

				pla
				sta CTSText4
		
				PushWord #0000
				PushWord #$F800
				PushLong #fqdnptr
				_TCPIPMangleDomainName	; mangle entered domain name
				pla						; port number; trash

				PushWord #0000
				PushLong #fqdnptr
				_TCPIPValidateIPString
				pla
				beq resolve				; engage DNS if this wasn't an ip address
				PushLong #ipaddrip
				PushLong #fqdnptr
				_TCPIPConvertIPToHex	; else just convert to long
				bra Connect				; and on with the show

resolve			PushLong #fqdnptr
				PushLong #ipaddr
				_TCPIPDNRNameToIP		; resolve fqdn
ResolvLoop		PushWord #0000			; check for mouse events (cancel)
				PushWord #$0006			; with _EventAvail
				PushLong #EventRec		; because _ModalDialog will block
				_EventAvail
				pla
				beq noevent
				PushWord #0000
				PushLong #00000000
				_ModalDialog
				pla
				cmp #2
				beq CTSClose2
noevent			_TCPIPPoll
				lda ipaddr
				cmp #$0001
				beq Connect				; continue to Connect if resolved okay
				cmp #$0000
				beq ResolvLoop			; pending; stay in 'Resolving' loop
				bra CTSBadDNS
CTSClose2		PushLong CTSWinPtr
				_CloseDialog
				rts
CTSBadDNS		PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSText5
				_SetIText
				bra CTSClose2

Connect			PushWord #0000
				PushLong ipaddrip
				PushLong #destaddr
				PushWord #0000
				_TCPIPConvertIPToASCII
				pla

				PushWord CTSText6		; preserve old length byte (word)

				sep #$30
				mx %11
				lda destaddr
				tax
				clc
				adc CTSText6
				sta CTSText6
ipcopy			lda destaddr,x
				sta ascdest-1,x
				dex
				bne ipcopy
				rep #$30
				mx %00

				PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSText6
				_SetIText				; change status to 'Connecting'

				pla
				sta CTSText6			; restore length byte

				PushWord #0000			; space for result
				PushWord AppMMID		; our Memory Manager handle
				PushLong ipaddrip		; ipaddress to connect to
				PushWord #0445			; try port 445 first
				PushWord #0000			; default TOS: 0000
				PushWord #$0040			; default TTL: 64
				_TCPIPLogin
				pla
				sta MyIPID

				PushWord #$0000
				PushWord MyIPID
				_TCPIPOpenTCP
				pla
				beq ConnectLoop

				jmp CTSClose3

ConnectLoop		PushWord #0000
				PushWord #$0006
				PushLong #EventRec
				_EventAvail
				pla
				beq noevent2
				PushWord #0000			; space for result
				PushLong #00000000		; nil filter procedure
				_ModalDialog
				pla
				cmp #2
				bne noevent2
				jmp CTSClose3
noevent2		_TCPIPPoll
				PushWord #0000
				PushWord MyIPID
				PushLong #statbuf
				_TCPIPStatusTCP
				pla
				beq notbroke
				jmp CTSClose3			; something broke
notbroke		lda statbuf
				cmp #$0004
				beq estab
				cmp #$0000
				beq closed_trampoline
				bra ConnectLoop

;
; SMB_Negot_Prot
;

estab			PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSText9
				_SetIText

				PushLong MyIPID
				jsr SMB_Init			; feed our Marinetti IPID into SMB system
				PullLong MySMBHandle		; get back our SMB handle

				PushLong MySMBHandle		; SMB_Negotiate wants our SMB handle
				jsr SMB_Negotiate		; and hopefully we send a SMB Negotiate Protocol Request and get a response

				bra ct_far
closed_trampoline	jmp closed
ct_far

sendloop2		PushWord #0000
				PushWord #$0006
				PushLong #EventRec
				_EventAvail
				pla
				beq noevent3
				PushWord #0000
				PushLong #00000000
				_ModalDialog
				pla
				cmp #2
				bne noevent3
				jmp CTSClose3

noevent3		PushLong MySMBHandle
				jsr SMB_Negotiate_Poll
				pla						; get negotiation status
				bcc sendloop2

;
; SMB_Setup_ANDX
;

login			PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSTextB
				_SetIText

				PushLong MySMBHandle
				jsr SMB_SetupAndX

sendloop3		PushWord #0000
				PushWord #$0006
				PushLong #EventRec
				_EventAvail
				pla
				beq noevent4
				PushWord #0000
				PushLong #00000000
				_ModalDialog
				pla
				cmp #2
				bne noevent4
				jmp CTSClose3

noevent4		PushLong MySMBHandle
				jsr SMB_Setup_Poll
				pla						; get negotiation status
				bcc sendloop3
				
;
; SMB_Tree_ANDX
;

; fun fact: change 'treex' to 'tree' to coredump Merlin32 v1.0
; inside a65816_Line.c function CheckForDuplicatedLabel()
treex			PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSTextC
				_SetIText
				
				PushLong MySMBHandle
				jsr SMB_TreeAndX

sendloop4		PushWord #0000
				PushWord #$0006
				PushLong #EventRec
				_EventAvail
				pla
				beq noevent5
				PushWord #0000
				PushLong #00000000
				_ModalDialog
				pla
				cmp #2
				bne noevent5
				jmp CTSClose3

noevent5		PushLong MySMBHandle
				jsr SMB_TreeX_Poll
				pla
				bcc sendloop4

;
; SMB_OpenFile
;

openf			PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSTextC
				_SetIText
				
				PushLong MySMBHandle
				PushLong #SMB_target_file
				jsr SMB_OpenFile

sendloop5		PushWord #0000
				PushWord #$0006
				PushLong #EventRec
				_EventAvail
				pla
				beq noevent6
				PushWord #0000
				PushLong #00000000
				_ModalDialog
				pla
				cmp #2
				bne noevent6
				jmp CTSClose3

noevent6		PushLong MySMBHandle
				jsr SMB_OpenFile_Poll
				bcc sendloop5
				tax
				pla
				phx								; save SFID

;
; SMB_ReadFile
;

readf			PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSTextD
				_SetIText

				; SFID already on stack still
				PushLong MySMBHandle
				PushLong #$E12000				; read buffer = SHR screen
				PushLong #$8000					; size of read buffer
				PushLong #0000					; offset into file
				jsr SMB_ReadFile

sendloop6		PushWord #0000
				PushWord #$0006
				PushLong #EventRec
				_EventAvail
				pla
				beq noevent7
				PushWord #0000
				PushLong #00000000
				_ModalDialog
				pla
				cmp #2
				bne noevent7
				jmp CTSClose3

noevent7		PushLong MySMBHandle
				jsr SMB_ReadFile_Poll
				bcc sendloop6

;
; Display what was downloaded
;

				; squish length byte a little closer to the string
				lda SMB_input+SMB_header_size+25
				xba
				sta SMB_input+SMB_header_size+25
				
				PushLong #SMB_input+SMB_header_size+26
				jsr InfoDialog

				jmp SMB_staging_brk

;
; Closed
;

closed			PushLong CTSWinPtr
				PushWord #1350
				PushLong #CTSTextA
				_SetIText

;
; Kill dialog window and TCPIP connection
;

CTSClose3		PushWord #MyIPID
				_TCPIPLogout			; get rid of our IPID
				PushLong CTSWinPtr		; and close the dialog window
				_CloseDialog
				rts

*
* Menu Dispatcher
*
MenuDispatch	lda TaskData
				and #$00ff
				asl
				tax
				jsr (MenuDispatchTbl,x)

				PushWord #0000
				PushWord TaskData+2
				_HiliteMenu
				rts

*
* Information Dialog
*
InfoDialog		plx					; preserve return address
				PullLong dialogMsg
				phx					; put return back on stack
				pha					; room for result
				PushLong #OurAlert	; pointer to dialog
				PushLong #$0000
				_NoteAlert
				pla					; get the item selected
				rts

*
* Non-DP Variables
*
QuitFlag		dw 00,00
ScreenColorByte	dw 00
MyIPID			dw 00				; Marinetti IPID
MySMBHandle		dw 00,00			; SMB driver handle
CTSWinPtr		dw 00,00			; Connect To Server grafport ptr
fqdnptr			ds 101				; 101 bytes for user-entered hostname
ipaddr			dw 00				; DNS resolution status
ipaddrip		dw 00,00			; Resolved IP address
ascaddr			ds 16				; My IP address, in ASCII
destaddr		ds 16				; Destination IP address, in ASCII
statbuf			ds 2				; TCP state, statbuf+0
				ds 2				; ICMP error code, statbuf+2
				ds 4				; sendq size, statbuf+4
				ds 4				; recvq size, statbuf+8
				ds 4				; dest ip
				ds 2				; dest port
				ds 2				; connection type
				ds 2				; accept count
readbuf			ds 4				; rrBuffCount
				ds 4				; rrBuffHandle
				ds 2				; rrMoreFlag
				ds 2				; rrPushFlag
				ds 2				; rrUrgentFlag

*
* Tool List
*
ToolList     	dw 15				; Tool Count
				dw 01,00			; Tool Locator, any vers
				dw 02,00			; Memory Manager, any vers
				dw 03,00			; Misc Tools, any vers
				dw 04,00			; QuickDraw II, any vers
				dw 06,00			; Event Manager, any vers
				dw 14,00			; Window Manager, any vers
				dw 16,00			; Control Manager, any vers
				dw 15,00			; Menu Manager, any vers
				dw 05,00			; Desk Manager, any vers
				dw 08,00			; Sound Manager, any vers
				dw 20,00			; Line Edit, any vers
				dw 21,00			; Dialog Manager, any vers
				dw 54,00			; Marinetti, any vers
				dw 128,00			; HashTool, any vers (MD2/MD4/MD5/SHA1 hashing for NTLMv2)
				dw 129,00			; Crypto tool, any vers (DES for NTLMv1)
ToolListEnd

*
* Menu Structures
*

MenuDispatchTbl	dw About
				dw Quit
				dw TCPIPTest
				dw ConnectToSvr

MenuTbl			dw 4				; count
				dw Menu1
				dw Menu2
				dw Menu3
				dw Menu4
MenuTblEnd

Menu1			asc '>>@\XN1'00
				asc '--About This Program...\N256*??'00
				asc '---\D'00
				asc '>'

Menu2			asc '>>  File  \N2'00
				asc '--Quit\N257*Qq'00
				asc '>'

Menu3			asc '>>  Edit  \N3D'00
				asc '--Undo\N250V*Zz'00
				asc '--Cut\N251*Xx'00
				asc '--Copy\N252*Cc'00
				asc '--Paste\N253V*Vv'00
				asc '--Clear\N254'00
				asc '>'

Menu4			asc '>>  Test  \N4'00
				asc '--TCP/IP Test\N258*Tt'00
				asc '--Connect to Server\N259*Uu'00
				asc '>'

*
* Event Record
*
EventRec
Event_What   	ds 2
Event_Msg		ds 4
Event_When		ds 4
Event_Where		ds 4
Event_Mods		ds 2
TaskData		ds 4
TaskMask		dw $1fff,$0000

*
* Strings
*
OneMoment		asc 'One Moment Please...'00
MarinettiGood	str 'Marinetti is Up.'
MarinettiBad	str 'Marinetti is Not Up.'
AboutString		str 'TCPIP Test Program'
But1			str 'OK'

*
* Connect To Server Dialog
*
CTSRect			dw 30,120,120,520			; bounds rect
CTSVis			dw 0001						; visible
CTSRef			dw 0000,0000				; refcon
CTSiptr1		adrl CTSItem1				; string 'Host To Connect To'
CTSiptr2		adrl CTSItem2				; edit line
CTSiptr3		adrl CTSItem3				; connect button
CTSiptr4		adrl CTSItem4				; cancel button
CTSiptr5		adrl CTSItem5				; status line
CTSiptr6		adrl CTSItem6				; My IP Address
CTSiptr7		adrl CTSItem7				; "My Ip Address: "
				dw 0000						; end of the line

CTSItem1		dw 1348						; id
				dw 5,10,15,300				; bounds
				dw statText+$8000			; type + disabled
				adrl CTSText1
				dw 00						; item value
				dw 00						; item flag
				adrl 00						; end
CTSText1		str 'Address Of CIFS Server To Connect To:'

CTSItem2		dw 1349						; id
				dw 25,10,38,200				; bounds
				dw editLine
				adrl CTSText2
				dw 100
				dw 00
				adrl 00
CTSText2		str 'example.com'

CTSItem3		dw 1
				dw 50,10,60,100
				dw buttonItem
				adrl But2
				dw 00
				dw 00
				adrl 00
But2			str 'Connect'

CTSItem4		dw 2
				dw 50,111,60,210
				dw buttonItem
				adrl But3
				dw 00
				dw 00
				adrl 00
But3			str 'Cancel'

CTSItem5		dw 1350						; id
				dw 65,10,75,300				; bounds
				dw statText+$8000			; type + disabled
				adrl CTSText3
				dw 00
				dw 00
				adrl 00
CTSText3		str 'Status: Awaiting Input'
CTSText4		str 'Status: Resolving Hostname '
inhost			ds 101
CTSText5		str 'Status: Resolution Failure'
CTSText6		str 'Status: Connecting to '
ascdest			ds 20
CTSText9		str 'Status: Connected, CIFS negotiating'
CTSTextA		str 'Status: Connect Failed'
CTSTextB		str 'Status: Logging In'
CTSTextC		str 'Status: Mounting Share'
CTSTextD		str 'Status: Opening \\testfile'
CTSTextE		str 'Status: Reading testfile'
ascdata			ds 100						; we download this from testfile

CTSItem6		dw 1360						; id
				dw 77,120,87,300			; bounds
				dw statText+$8000
				adrl CTSText7
				dw 00
				dw 00
				adrl 00
CTSText7		str '0.0.0.0'

CTSItem7		dw 1361						; id
				dw 77,10,87,120				; bounds
				dw statText+$8000
				adrl CTSText8
				dw 00
				dw 00
				adrl 00
CTSText8		str 'My IP Address:'

*
* TCPIP Test Output Dialog
*
OurAlert		dw 30,120,80,520			; bounds rect
				dw 2374						; id
				db $80
				db $80
				db $80
				db $80
				adrl item1
				adrl item2
				adrl 00

item1			dw 1						; id
				dw 25,320,0,0				; bounds rect for button
				dw buttonItem				; type
				adrl But1					; item descriptor
				dw 00						; item value
				dw 00						; item flag
				adrl 00						; item color

item2			dw 1348						; id
				dw 11,72,200,640			; bounds rect for message
				dw statText+$8000			; type + disabled
dialogMsg		adrl MarinettiGood			; item descriptor
				dw 00						; item value
				dw 00						; item flag
				adrl 00						; item color

*
* GS/OS Quit Params
*
QParams			ds 4
				ds 4

*** CIFS / SMB internals only past this point (will eventually be its own linker segment)

*
* SMB equates (should move into Library/E16.CIFS.Macs.s eventually)
*

* offsets into SMB header
SMB_offset_tcplength	= 1					; tcp length: all dgram length including header
SMB_offset_proto	= 0+4
SMB_offset_cmd		= 4+4
SMB_offset_ntstatus	= 5+4
SMB_offset_eclass	= 5+4
SMB_offset_flags	= 9+4
SMB_offset_flags2	= 10+4
SMB_offset_extra	= 12+4
SMB_offset_tid		= 24+4
SMB_offset_pid		= 26+4
SMB_offset_uid		= 28+4
SMB_offset_mid		= 30+4
SMB_header_size		= 32+4					; SMB headers are always 32 bytes long

* message / commands
NBT_session_msg		= 00
SMB_neg_protocol	= $72
SMB_setup_ANDX		= $73
SMB_treec_ANDX		= $75

* keepalive
NBT_keepalive_msg	= $85
keepalive_size		= 4

* SMBTrans2
SMB_trans2			= $32
SMB_open2			= 0
SMB_find_first2		= 1
SMB_find_next2		= 2
SMB_query_fs_info	= 3
SMB_query_path_info	= 5
SMB_set_path_info	= 6
SMB_query_file_info	= 7
SMB_set_file_info	= 8
SMB_create_dir		= 13
SMB_find_close2		= $34
SMB_query_file_all_info	= $107

* File I/O
SMB_open_ANDX		= $2d
SMB_write_ANDX		= $2f
SMB_read_ANDX		= $2e
SMB_close			= $04

* SMB_COM
SMB_COM_create_directory		= $00
SMB_COM_delete_directory		= $01
SMB_COM_delete					= $06
SMB_COM_rename					= $07
SMB_COM_query_information_disk	= $80

* TRANS2 offsets
T2_word_cnt			= SMB_header_size
T2_prm_cnt			= T2_word_cnt+1
T2_data_cnt			= T2_prm_cnt+2
T2_maxprm_cnt		= T2_data_cnt+2
T2_maxbuffer		= T2_maxprm_cnt+2
T2_setup_cnt		= T2_maxbuffer+2
T2_sprm_cnt			= T2_setup_cnt+10
T2_sprm_ofs			= T2_sprm_cnt+2
T2_sdata_cnt		= T2_sprm_ofs+2
T2_sdata_ofs		= T2_sdata_cnd+2
T2_ssetup_cnt		= T2_sdata_ofs+2
T2_sub_cmd			= T2_ssetup_cnt+2
T2_byte_cnt			= T2_sub_cmd+2

SMB_proto1			= $53ff						; $ff + 'S'
SMB_proto2			= $424d						; 'MB'

SMB_max_net_read_size	= 16384
SMB_max_net_write_size	= 4096
SMB_max_transmit_size	= 16384

CAP_large_files		= $00000008					; 64-bit file sizes and offsets supported
CAP_unicode			= $00000004					; unicode supported

CIFS_flags1			= $08						; paths are caseless
CIFS_flags2_unicode	= $8001						; server may return long components in paths in the response - Unicode supported
CIFS_flags2			= $0001						; server may return long components in paths in the response - ASCII supported

SMB_connhandles_max	= 8
SMB_filehandles_max	= (32*SMB_connhandles_max)

SMB_objtype_handle	= 7

* NBT session service packet type codes
SESS_msg			= $0
SESS_req			= $81
SESS_pos_resp		= $82
SESS_neg_resp		= $83
SESS_retarget		= $84
SESS_keepalive		= $85

SMB_maxpath			= 4096

* SMB error codes
SMB_success			= 0
SMB_error			= $ffff						; -1
SMB_bad_protocol	= $fffe						; -2
SMB_bad_command		= $fffd						; -3
SMB_proto_fail		= $fffc						; -4
SMB_not_user		= $fffb						; -5
SMB_bad_keylen		= $fffa						; -6
SMB_bad_datalen		= $fff9						; -7
SMB_bad_logindata	= $fff8						; -8

* SMB file open function
SMB_of_open			= 1
SMB_of_truncate		= 2
SMB_of_create		= 16

* FileSearch
SMB_srch_readonly	= 1
SMB_srch_hidden		= 2
SMB_srch_system		= 4
SMB_srch_volume		= 8
SMB_srch_directory	= 16
SMB_srch_archive	= 32

* SMB file access modes
SMB_open_reading	= 0
SMB_open_writing	= 1
SMB_open_readwrite	= 2
SMB_open_compatible	= 0
SMB_deny_readwrite	= $10
SMB_deny_write		= $20
SMB_deny_read		= $30
SMB_deny_none		= $40

*
* SMB variable space
*

* SMB DP variables
SMB_sessid			= 8
SMB_tmp1			= 12
SMB_tmp2			= 14
SMB_tmp3			= 16
SMB_tmp4			= 18
SMB_tmp5			= 20
SMB_tmp6			= 22
SMB_tmp7			= 24
SMB_tmp8			= 26

* SMB session information
* TODO dynamically allocate these - see SMB_Init
SMB_sess_begin
SMB_sess_ipid		dw 0					; Marinetti IPID
SMB_sess_tid		dw 0					; SMB TID
SMB_sess_pid		dw $dead				; SMB PID
SMB_sess_uid		dw 0					; SMB UID
SMB_sess_mid		dw $0001				; SMB MID
SMB_sess_skey		dw 0,0					; sKey
SMB_sess_caps		dw 0,0					; capabilities
SMB_sess_maxbuffer	dw 0,0					; max buffer
SMB_sess_seclvl		dw 0					; server security level
SMB_sess_maxmpx		dw 0					; server max pending outstanding requests
SMB_sess_maxvcs		dw 0					; server max virtual circuits
SMB_sess_challenge_used	dw 0					; challenge Used?
SMB_sess_challenge	dw 0,0,0,0				; challenge
SMB_sess_domain		ds 32					; sloppy 32 byte buffer to hold SMB domain

* Strings
SMB_dialect			asc 02'NT LM 0.12'00		; the only dialect we're gonna speak
SMB_os				asc 'GS/OS'00				; native Operating System
SMB_lanman			asc 'Apple IIgs'00			; native LAN Manager
SMB_lm_username		asc 'PI'00,00,00,00,00,00,00,00,00,00,00,00 ; lanman hash login username
SMB_lm_password		asc 'APPLE2'00,00,00,00,00,00,00,00 ; lanman hash login password
SMB_lm_magic		asc 'KGS!@#$%'				; lanman hash magic DES crypt string
SMB_lm_hash			ds 21						; LM Hash, actually 16 bytes but the extra zeroes make response easier to generate
SMB_lm_response		ds 24						; LM Response
SMB_target_tree		asc '\\LIVINGROOM\GSFILES'00	; remote tree to connect to
SMB_target_svc		asc '?????'00					; service type (wildcard)
SMB_target_file		asc '\TESTFILE'00			; file to download

* SMB packet staging area
* TODO these will probably be dynamically allocated too?
*  also SMB_max_transmit size should go up to 65535 and use an allocated bank like Marinetti does
SMB_staging_brk		brk 00						; jump here to crash somewhere where you can easily inspect the generated SMB datagram
SMB_staging			ds SMB_max_transmit_size+2
SMB_input_brk		brk 00
SMB_input			ds SMB_max_net_read_size	; TODO Memory Manage this eh

*
* SMB Subroutines
*

* SMB_Init - Call me to tell me about your Marinetti IPID and get a session handle returned for future SMB calls
* Arguments:
*    Marinetti IPID (one word, on stack)
* Returns:
*    SMB session handle (two words, on stack)
*    Carry flag set if error
* TODO: make this support multiple sessions in the future
SMB_Init		plx							; return address
				pla
				sta SMB_sess_ipid
				PushLong #SMB_sess_begin
				phx							; saved return address
				clc
				rts

* SMB_Negotiate - Call me when you've opened a TCPIP connection and want to perform Negotiate Protocol Request
* Arguments:
*    SMB session handle (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_Negotiate	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_neg_protocol	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information
				
				sep $30
				mx %11
				ldy #0
				ldx #0
dialect_copy	lda SMB_dialect,x			; 8-bit copy of dialect string into SMB staging area
				beq dialect_done
				sta SMB_staging+SMB_header_size+3,x
				inx
				iny
				bra dialect_copy
dialect_done	sta SMB_staging+SMB_header_size+3,x	; do write the trailing zero
				inx
				iny
				rep $30
				mx %00
				
				tya
				sta SMB_staging+SMB_header_size+1		; save byte count
				
				clc
				adc #SMB_header_size+3
				pha							; 'length' parameter for _SMB_Send
				dec
				dec
				dec
				dec
				xba
				sta SMB_staging+SMB_offset_tcplength+1			; save length for naked-TCP dgram
				
				jsr _SMB_Send
				clc
				rts

* SMB_Negotiate_Poll - Call me until I tell you to stop, to receive and complete SMB negotiation
* Arguments:
*   SMB session handle (two words, on stack)
* Things I return on stack:
*   Negotiation status (word)
*     $0000 - Negotiation proceeding
*     $0001 - Negotiation finished
*     $0002 - Negotiation failed
*   Carry flag set means you can stop calling me
SMB_Negotiate_Poll
				plx							; our return address
				PullLong SMB_sessid			; your smb sessid
				phx

				_TCPIPPoll

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushLong #statbuf
				_TCPIPStatusTCP				; see if marinetti has anything for us
				pla
				cmp #terrNOCONNECTION
				beq nf_trampoline
				cmp #terrBADIPID
				beq nf_trampoline
				lda statbuf+8				; get recvq size, low word
				cmp #0000					; yeah i know. for clarity.
				beq np_trampoline			; poll us again later, marinetti got nothing

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushWord #0000				; bufftype: static pre-allocated buffer
				PushLong #SMB_input			; where it's all goin
				PushLong #SMB_max_net_read_size
				PushLong #readbuf
				_TCPIPReadTCP

				pla
				cmp #terrNOCONNECTION
				beq nf_trampoline
				cmp #terrBADIPID
				beq nf_trampoline
								
				jsr _SMB_Check				; do basic check to make sure we received SMB data
				bcs np_trampoline			; if not, wait for them to send again i guess
				
`				lda SMB_input+SMB_offset_cmd
				cmp #SMB_neg_protocol
				bne np_trampoline			; punt if not protocol negotiation reply
				
				lda SMB_input+SMB_offset_eclass
				cmp #0000
				bne nf_trampoline				; they returned an error, kbye
				
				lda SMB_input+SMB_header_size
				and #$ff
				cmp #17
				bne nf_trampoline				; they should always return 17 words
				
				lda SMB_input+SMB_header_size
				xba
				and #$ff
				cmp #00
				bne nf_trampoline			; they should always select dialect 0

				bra nft_far
nf_trampoline	jmp nego_failed
np_trampoline	jmp nego_proceeding
nft_far
				
				lda SMB_input+SMB_header_size+3
				and #$01
				ldy #SMB_sess_seclvl-SMB_sess_begin
				sta [SMB_sessid],y			; save session security level (1=user level, 0=share level)

				lda SMB_input+SMB_header_size+4
				and #$ff
				ldy #SMB_sess_maxmpx-SMB_sess_begin
				sta [SMB_sessid],y			; save session MaxMPX
				
				lda SMB_input+SMB_header_size+6
				and #$ff
				ldy #SMB_sess_maxvcs-SMB_sess_begin
				sta [SMB_sessid],y			; save session MaxVCS
				
				lda SMB_input+SMB_header_size+8
				ldy #SMB_sess_maxbuffer-SMB_sess_begin
				sta [SMB_sessid],y			; save session maxbuffer
								
				lda SMB_input+SMB_header_size+16
				ldy #SMB_sess_skey-SMB_sess_begin
				sta [SMB_sessid],y			; save skey
				
				lda SMB_input+SMB_header_size+20
				ldy #SMB_sess_caps-SMB_sess_begin
				sta [SMB_sessid],y
				iny
				iny
				lda SMB_input+SMB_header_size+22
				sta [SMB_sessid],y			; save server capabilities

				; TODO serverTime
				
				lda SMB_input+SMB_header_size+33
				and #$ff
				cmp #8
				bne challenge_used
				cmp #0
				bne nego_failed				; should be either 8 or zero
				lda #$0000
				ldy #SMB_sess_challenge_used
				sta [SMB_sessid],y
				bra getdomain

challenge_used	lda #$0001
				ldy #SMB_sess_challenge_used-SMB_sess_begin
				sta [SMB_sessid],y
				
				lda SMB_input+SMB_header_size+37
				ldy #SMB_sess_challenge-SMB_sess_begin
				sta [SMB_sessid],y
				iny
				iny
				lda SMB_input+SMB_header_size+39
				sta [SMB_sessid],y
				iny
				iny
				lda SMB_input+SMB_header_size+41
				sta [SMB_sessid],y
				iny
				iny
				lda SMB_input+SMB_header_size+43
				sta [SMB_sessid],y			; save 8-byte challenge

getdomain		ldy #SMB_sess_domain-SMB_sess_begin
				ldx #00
gdloop			lda SMB_input+SMB_header_size+45,x
				and #$00ff
				cmp #0000
				beq eod
				sta [SMB_sessid],y			; if this looks off to you, see how
				iny							; the domain name is actually encoded
				inx							; W\00O\00R\00K\00G\00R\00O\00U\00P\00
				inx
				bra gdloop

eod				sta [SMB_sessid],y			; save terminating zero				

nego_finished	plx							; our return address
				PushWord #0001				; finished!
				phx
				sec
				rts

nego_failed		plx							; our return address
				PushWord #0002				; failure
				phx
				sec
				rts

nego_proceeding	plx							; our return address
				PushWord #0000				; in progress
				phx
				clc
				rts

*
* SMB_SetupAndX - Next step after negotiating protocol, includes authentication
* Arguments:
*    SMB session handle (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_SetupAndX	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_setup_ANDX	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

				sep $30
				mx %11
				lda #13
				sta SMB_staging+SMB_header_size	; word count
				
				lda #$ff
				sta SMB_staging+SMB_header_size+1	; next AndX
				
				lda #00
				sta SMB_staging+SMB_header_size+2	; reserved
				rep $30
				mx %00
				
				ldy #SMB_sess_maxbuffer-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_header_size+5	; max buffer size
				
				ldy #SMB_sess_maxmpx-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_header_size+7	; max MPX
				
				ldy #SMB_sess_maxvcs-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_header_size+9	; max VCS
				
				ldy #SMB_sess_skey-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_header_size+11	; session key
				lda #0
				sta SMB_staging+SMB_header_size+13	; session key (upper, should always be zero?)
				
				lda #24
				sta SMB_staging+SMB_header_size+15	; password length (case insensitive)
				
				lda #0
				sta SMB_staging+SMB_header_size+17	; password length (case sensitive/NTLM)
				
				lda #0
				sta SMB_staging+SMB_header_size+19	; reserved
				sta SMB_staging+SMB_header_size+21	; reserved
				
				ldy #SMB_sess_caps-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_header_size+23
				iny
				iny
				lda [SMB_sessid],y
				sta SMB_staging+SMB_header_size+25	; session capabilities
				
				jsr SMB_LM_Hash					; generate NTLMv1 Hash
				jsr SMB_LM_Response				; generate NTLMv1 Response
				
				lda #0
				sta SMB_tmp5					; initialize pointer
				
				; Case Insensitive Password (LM Response)
				PushLong #SMB_lm_response					; source
				pea #^SMB_staging
				lda #SMB_staging+SMB_header_size+29
				clc
				adc SMB_tmp5
				pha
				ldx #24
				jsr _strncpy
				lda #24
				clc
				adc SMB_tmp5
				sta SMB_tmp5
				
				; Case Sensitive Password (NTLM Response)
				clc
				adc #24
				sta SMB_tmp5								; skip it / zero it for now
				
				; Account
				PushLong #SMB_lm_username					; source
				pea #^SMB_staging							; destination
				lda #SMB_staging+SMB_header_size+29
				clc
				adc SMB_tmp5
				pha
				jsr _strcpy
				tya
				clc
				adc SMB_tmp5
				sta SMB_tmp5
				
				; Domain
				PushLong #SMB_sess_domain					; source
				pea #^SMB_staging							; destination
				lda #SMB_staging+SMB_header_size+29
				clc
				adc SMB_tmp5
				pha
				jsr _strcpy
				tya
				clc
				adc SMB_tmp5
				sta SMB_tmp5
				
				; OS
				PushLong #SMB_os							; source
				pea #^SMB_staging							; destination
				lda #SMB_staging+SMB_header_size+29
				clc
				adc SMB_tmp5
				pha
				jsr _strcpy
				tya
				clc
				adc SMB_tmp5
				sta SMB_tmp5
				
				; LanMan
				PushLong #SMB_lanman
				pea #^SMB_staging							; destination
				lda #SMB_staging+SMB_header_size+29
				clc
				adc SMB_tmp5
				pha
				jsr _strcpy
				tya
				clc
				adc SMB_tmp5
				sta SMB_tmp5
				
				sta SMB_staging+SMB_header_size+27		; update byte count
				
				clc
				adc #SMB_header_size+29
				pha							; 'length' parameter for _SMB_Send
				dec
				dec
				dec
				dec
				xba
				sta SMB_staging+SMB_offset_tcplength+1			; save length for naked-TCP dgram

				jsr _SMB_Send							; send our reply!
				clc
				rts

* SMB_LM_Hash - operate on the SMB_lm_username and SMB_lm_password values to produce
* SMB_lm_hash, using V1 DES-based operation. Requires TOOL129 (Crypto Tool)
*
* The password has been uppercased and chopped to 14 bytes, with 0x00 padding
* We take it in two 7-byte pieces, add parity, and use each piece to DES-encrypt the string
* 'KGS!@#$%'. The result is the LM hash.
SMB_LM_Hash		PushLong #SMB_tmp1					; SMB_tmp1 will have our parity-enhanced key
				PushLong #SMB_lm_password			; key to add parity to
				Tool $0A81							; DESAddParity

				PushLong #SMB_lm_hash				; where DES-encrypted data will go
				PushLong #SMB_tmp1					; parity-enhanced key
				PushLong #SMB_lm_magic				; the magic string we're encrypting
				PushWord #0000						; operation: encrypt
				Tool $0981							; DESCipher

				; now do the same thing with the second 7 bytes of the password

				PushLong #SMB_tmp1
				PushLong #SMB_lm_password+7
				Tool $0A81							; DesAddParity

				PushLong #SMB_lm_hash+8
				PushLong #SMB_tmp1
				PushLong #SMB_lm_magic
				PushWord #0000
				Tool $0981

				; SMB_lm_hash should now contain the NTLMv1 hash of the password
				clc
				rts

* SMB_LM_Response - operate on SMB_lm_hash and 8-byte challenge to produce
* SMB_lm_response, using V1 DES-based operation. Requires TOOL129 (Crypto Tool)
*
* LM Hash is padded out to 21 bytes, each of which gets parity added and is
* used to DES encrypt the session challenge. The result is the LM response.
SMB_LM_Response
				PushLong #SMB_tmp1				; SMB_tmp1 will have our parity-enhanced key
				PushLong #SMB_lm_hash			; key to add parity to
				Tool $0A81						; DESAddParity
				
				PushLong #SMB_lm_response		; where the DES-encrypted data will go
				PushLong #SMB_tmp1				; parity-enhanced key
				ldy #SMB_sess_challenge-SMB_sess_begin
				lda [SMB_sessid],y				; PushLong [SMB_sessid],y
				pha
				iny
				iny
				lda [SMB_sessid],y
				pha
				PushWord #0000					; operation: encrypt
				Tool $0981						; DESCipher
				
				; second 7 bytes of hash
				
				PushLong #SMB_tmp1
				PushLong #SMB_lm_hash+7
				Tool $0A81						; DESAddParity
				
				PushLong #SMB_lm_response+8
				PushLong #SMB_tmp1
				ldy #SMB_sess_challenge-SMB_sess_begin
				lda [SMB_sessid],y				; PushLong [SMB_sessid],y
				pha
				iny
				iny
				lda [SMB_sessid],y
				pha
				PushWord #0000
				Tool $0981						; DESCipher
				
				; third 7 bytes of hash
				
				PushLong #SMB_tmp1
				PushLong #SMB_lm_hash+14
				Tool $0A81						; DesAddParity
				
				PushLong #SMB_lm_response+16
				PushLong #SMB_tmp1
				ldy #SMB_sess_challenge-SMB_sess_begin
				lda [SMB_sessid],y
				pha
				iny
				iny
				lda [SMB_sessid],y
				pha
				PushWord #0000
				Tool $0981						; DESCipher
				
				clc
				rts

* SMB_Setup_Poll - Call me until I tell you to stop, to receive and complete SMB setup
* Arguments:
*   SMB session handle (two words, on stack)
* Things I return on stack:
*   Setup status (word)
*     $0000 - Setup proceeding
*     $0001 - Setup finished
*     $0002 - Setup failed
*   Carry flag set means you can stop calling me
SMB_Setup_Poll
				plx							; our return address
				PullLong SMB_sessid			; your smb sessid
				phx

				_TCPIPPoll

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushLong #statbuf
				_TCPIPStatusTCP				; see if marinetti has anything for us
				pla
				cmp #terrNOCONNECTION
				beq sf_trampoline
				cmp #terrBADIPID
				beq sf_trampoline
				lda statbuf+8				; get recvq size, low word
				cmp #0000					; yeah i know. for clarity.
				beq sp_trampoline			; poll us again later, marinetti got nothing

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushWord #0000				; bufftype: static pre-allocated buffer
				PushLong #SMB_input			; where it's all goin
				PushLong #SMB_max_net_read_size
				PushLong #readbuf
				_TCPIPReadTCP

				pla
				cmp #terrNOCONNECTION
				beq sf_trampoline
				cmp #terrBADIPID
				beq sf_trampoline
								
				jsr _SMB_Check				; do basic check to make sure we received SMB data
				bcs sp_trampoline			; if not, wait for them to send again i guess

				bra sft_far
sf_trampoline	jmp setup_failed
sp_trampoline	jmp setup_proceeding
sft_far
				lda SMB_input+SMB_offset_cmd
				cmp #SMB_setup_ANDX
				bne sp_trampoline			; punt if not setup_ANDX reply
				
				lda SMB_input+SMB_offset_eclass
				cmp #0000
				bne sf_trampoline			; they returned an error, kbye
				
				lda SMB_input+SMB_offset_uid
				ldy #SMB_sess_uid-SMB_sess_begin
				sta [SMB_sessid],y			; save returned UID

* TODO save far end's OS, Lan Manager, and Workgroup?

setup_finished	plx							; our return address
				PushWord #0001				; finished!
				phx
				sec
				rts

setup_failed	plx							; our return address
				PushWord #0002				; failure
				phx
				sec
				rts

setup_proceeding
				plx							; our return address
				PushWord #0000				; in progress
				phx
				clc
				rts

*
* SMB_TreeAndX - Connect to the remote share
* Arguments:
*    SMB session handle (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_TreeAndX	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_treec_ANDX	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

				sep $30
				mx %11
				lda #4
				sta SMB_staging+SMB_header_size	; word count
				
				lda #$ff
				sta SMB_staging+SMB_header_size+1	; next AndX
				
				rep $30
				mx %00
				
				lda #0000
				sta SMB_staging+SMB_header_size+3	; reserved
				
				lda #0000
				sta SMB_staging+SMB_header_size+5	; flags
				
				lda #1
				sta SMB_staging+SMB_header_size+7	; password length (1 if user-level security)

				lda #0
				sta SMB_tmp5					; initialize pointer
				
				; Share password
				lda #00
				sta SMB_staging+SMB_header_size+11	; 0x00 (nul password, no share level security)
				inc SMB_tmp5
				
				; Target Tree
				PushLong #SMB_target_tree
				pea #^SMB_staging							; destination
				lda #SMB_staging+SMB_header_size+11
				clc
				adc SMB_tmp5
				pha
				jsr _strcpy
				tya
				clc
				adc SMB_tmp5
				sta SMB_tmp5
				
				; Target Service Type
				PushLong #SMB_target_svc
				pea #^SMB_staging							; destination
				lda #SMB_staging+SMB_header_size+11
				clc
				adc SMB_tmp5
				pha
				jsr _strcpy
				tya
				clc
				adc SMB_tmp5
				sta SMB_tmp5
				
				sta SMB_staging+SMB_header_size+9		; update byte count
				
				clc
				adc #SMB_header_size+11
				pha							; 'length' parameter for _SMB_Send
				dec
				dec
				dec
				dec
				xba
				sta SMB_staging+SMB_offset_tcplength+1			; save length for naked-TCP dgram

				jsr _SMB_Send							; send our reply!
				clc
				rts

* SMB_TreeX_Poll - Call me until I tell you to stop, to receive and complete SMB Tree_ANDX
* Arguments:
*   SMB session handle (two words, on stack)
* Things I return on stack:
*   Setup status (word)
*     $0000 - Setup proceeding
*     $0001 - Setup finished
*     $0002 - Setup failed
*   Carry flag set means you can stop calling me
SMB_TreeX_Poll
				plx							; our return address
				PullLong SMB_sessid			; your smb sessid
				phx

				_TCPIPPoll

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushLong #statbuf
				_TCPIPStatusTCP				; see if marinetti has anything for us
				pla
				cmp #terrNOCONNECTION
				beq tf_trampoline
				cmp #terrBADIPID
				beq tf_trampoline
				lda statbuf+8				; get recvq size, low word
				cmp #0000					; yeah i know. for clarity.
				beq tp_trampoline			; poll us again later, marinetti got nothing

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushWord #0000				; bufftype: static pre-allocated buffer
				PushLong #SMB_input			; where it's all goin
				PushLong #SMB_max_net_read_size
				PushLong #readbuf
				_TCPIPReadTCP

				pla
				cmp #terrNOCONNECTION
				beq tf_trampoline
				cmp #terrBADIPID
				beq tf_trampoline
								
				jsr _SMB_Check				; do basic check to make sure we received SMB data
				bcs tp_trampoline			; if not, wait for them to send again i guess

				bra tft_far
tf_trampoline	jmp treex_failed
tp_trampoline	jmp treex_proceeding
tft_far
				lda SMB_input+SMB_offset_cmd
				cmp #SMB_treec_ANDX
				bne tp_trampoline			; punt if not setup_ANDX reply
				
				lda SMB_input+SMB_offset_eclass
				cmp #0000
				bne tf_trampoline			; they returned an error, kbye
				
				lda SMB_input+SMB_offset_tid
				ldy #SMB_sess_tid-SMB_sess_begin
				sta [SMB_sessid],y			; save returned TID
				
* TODO save remote servicetype or filesystem type?

treex_finished	plx							; our return address
				PushWord #0001				; finished!
				phx
				sec
				rts

treex_failed	plx							; our return address
				PushWord #0002				; failure
				phx
				sec
				rts

treex_proceeding
				plx							; our return address
				PushWord #0000				; in progress
				phx
				clc
				rts

*
* SMB_OpenFile - Open a file on the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to filename (two words, on stack)
* Things I return on stack:
*    A = SMB filehandle id
*    Carry flag set if error
SMB_OpenFile	plx							; return address
				PullLong SMB_tmp7			; filename
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_open_ANDX		; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

				sep $30
				mx %11
				lda #15
				sta SMB_staging+SMB_header_size	; word count
				
				lda #$ff
				sta SMB_staging+SMB_header_size+1	; next AndX
				
				lda #$00
				sta SMB_staging+SMB_header_size+2   ; AndX Reserved
				
				rep $30
				mx %00
				
				lda #$0000
				sta SMB_staging+SMB_header_size+3	; AndX Offset
				
				lda #$0000
				sta SMB_staging+SMB_header_size+5	; Flags
				
				lda #SMB_open_reading
				sta SMB_staging+SMB_header_size+7	; Access Mode
				
				lda #0000
				sta SMB_staging+SMB_header_size+9	; SearchAttrib (6 = HIDDEN & SYSTEM?)
								
				lda #0000
				sta SMB_staging+SMB_header_size+11	; file attributes
				
				lda #0000
				sta SMB_staging+SMB_header_size+13	; file time (don't care, let server decide)
				sta SMB_staging+SMB_header_size+15
				
				lda #SMB_of_open
				sta SMB_staging+SMB_header_size+17	; openmode
				
				lda #0000
				sta SMB_staging+SMB_header_size+19	; allocation size
				
				lda #0000
				sta SMB_staging+SMB_header_size+21	; allocation size
				
				lda #0000
				sta SMB_staging+SMB_header_size+23  ; timeout
				lda #0000
				sta SMB_staging+SMB_header_size+25	; timeout
				
				lda #0000
				sta SMB_staging+SMB_header_size+27	; reserved[0] must be zero
				lda #0000
				sta SMB_staging+SMB_header_size+29	; reserved[1] must be zero
				
				lda #0000
				sta SMB_staging+SMB_header_size+31	; byte count
				
				lda #0
				sta SMB_tmp5					; initialize pointer
				
				; Target File
				PushLong SMB_tmp7						; source
				pea #^SMB_staging							; destination
				lda #SMB_staging+SMB_header_size+33
				clc
				adc SMB_tmp5
				pha
				jsr _strcpy
				tya
				clc
				adc SMB_tmp5
				sta SMB_tmp5

				sta SMB_staging+SMB_header_size+31		; update byte count
				
				clc
				adc #SMB_header_size+33
				pha							; 'length' parameter for _SMB_Send
				dec
				dec
				dec
				dec
				xba
				sta SMB_staging+SMB_offset_tcplength+1			; save length for naked-TCP dgram

				jsr _SMB_Send							; send our reply!
				clc
				rts

* SMB_OpenFile_Poll - Call me until I tell you to stop, to receive and complete SMB Tree_ANDX
* Arguments:
*   SMB session handle (two words, on stack)
* Things I return on stack:
*   Setup status (word)
*     $0000 - Setup proceeding
*     $0001 - Setup finished
*     $0002 - Setup failed
*   Carry flag set means you can stop calling me
SMB_OpenFile_Poll
				plx							; our return address
				PullLong SMB_sessid			; your smb sessid
				phx

				_TCPIPPoll

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushLong #statbuf
				_TCPIPStatusTCP				; see if marinetti has anything for us
				pla
				cmp #terrNOCONNECTION
				beq of_trampoline
				cmp #terrBADIPID
				beq of_trampoline
				lda statbuf+8				; get recvq size, low word
				cmp #0000					; yeah i know. for clarity.
				beq op_trampoline			; poll us again later, marinetti got nothing

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushWord #0000				; bufftype: static pre-allocated buffer
				PushLong #SMB_input			; where it's all goin
				PushLong #SMB_max_net_read_size
				PushLong #readbuf
				_TCPIPReadTCP

				pla
				cmp #terrNOCONNECTION
				beq of_trampoline
				cmp #terrBADIPID
				beq of_trampoline
								
				jsr _SMB_Check				; do basic check to make sure we received SMB data
				bcs op_trampoline			; if not, wait for them to send again i guess

				bra oft_far
of_trampoline	jmp openx_failed
op_trampoline	jmp openx_proceeding
oft_far
				lda SMB_input+SMB_offset_cmd
				cmp #SMB_open_ANDX
				bne op_trampoline			; punt if not setup_ANDX reply
				
				lda SMB_input+SMB_offset_eclass
				cmp #0000
				bne of_trampoline			; they returned an error, kbye
				
				lda SMB_input+SMB_header_size+5 ; saved returned sfid

openx_finished	plx							; our return address
				PushWord #0001				; finished!
				phx
				sec
				rts

openx_failed	plx							; our return address
				PushWord #0002				; failure
				phx
				sec
				rts

openx_proceeding
				plx							; our return address
				PushWord #0000				; in progress
				phx
				clc
				rts


*
* SMB_CloseFile - Close an open file on the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    A = SMB filehandle id
* Things I return on stack:
*    Carry flag set if error
SMB_CloseFile	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_close			; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_CreateDirectory - Create a directory on the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to filename (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_CreateDirectory
				plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_COM_create_directory	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_DeleteDirectory - Delete a directory on the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to filename (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_DeleteDirectory
				plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_COM_delete_directory	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_DeleteFile - Delete a file on the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to filename (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_DeleteFile	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_COM_delete	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_RenameFile - Rename a file on the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to old filename (two words, on stack)
*    Long pointer to new filename (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_RenameFile	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_COM_rename	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_DiskInformation - Get information about the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to disk statbuf (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_DiskInformation
				plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_COM_query_information_disk	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_ReadFile - Read an open file from the remote share
* Arguments:
*    SFID of open file (one word, on stack)
*    SMB session handle (two words, on stack)
*    Long pointer to read buffer (two words, on stack)
*    Size of read buffer (two words, on stack)
*    Offset into file (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_ReadFile	plx							; return address
				PullLong SMB_tmp7			; SMB_tmp7/SMB_tmp8 = offset
				PullLong SMB_tmp5			; SMB_tmp5/SMB_tmp6 = sizeof read_buffer
				PullLong SMB_tmp3			; SMB_tmp3/SMB_tmp4 = *read_buffer
				PullLong SMB_sessid
				PullWord SMB_tmp1			; SMB_tmp1 = SFID
				phx							; saved return address

				PushWord #SMB_read_ANDX		; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

				sep $30
				mx %11
				lda #12
				sta SMB_staging+SMB_header_size	; word count
				
				lda #$ff
				sta SMB_staging+SMB_header_size+1	; next AndX
				
				lda #$00
				sta SMB_staging+SMB_header_size+2   ; AndX Reserved
				
				rep $30
				mx %00

				lda #$0000
				sta SMB_staging+SMB_header_size+3	; AndX Offset
				
				lda SMB_tmp1
				sta SMB_staging+SMB_header_size+5	; SFID
				
				lda SMB_tmp7
				sta SMB_staging+SMB_header_size+7	; Offsetlow
				lda SMB_tmp8
				sta SMB_staging+SMB_header_size+9	; Offsetlow2
				
				lda #1024
				sta SMB_staging+SMB_header_size+11	; maxcountbytestoreturn
				lda #0000
				sta SMB_staging+SMB_header_size+13	; mincountbytestoreturn
				
				lda #0000
				sta SMB_staging+SMB_header_size+15	; timeout
				lda #0000
				sta SMB_staging+SMB_header_size+17	; timeout2
				
				lda #0000
				sta SMB_staging+SMB_header_size+19	; remaining
				
				lda #0000
				sta SMB_staging+SMB_header_size+21	; offsethigh
				lda #0000
				sta SMB_staging+SMB_header_size+23	; offsethigh2
				
				lda #0000
				sta SMB_staging+SMB_header_size+25	; byte count (always 0)

				clc
				adc #SMB_header_size+27
				pha							; 'length' parameter for _SMB_Send
				dec
				dec
				dec
				dec
				xba
				sta SMB_staging+SMB_offset_tcplength+1			; save length for naked-TCP dgram

				jsr _SMB_Send							; send our reply!
				clc
				rts

* SMB_ReadFile_Poll - Call me until I tell you to stop, to receive and complete SMB Tree_ANDX
* Arguments:
*   SMB session handle (two words, on stack)
* Things I return on stack:
*   Setup status (word)
*     $0000 - Setup proceeding
*     $0001 - Setup finished
*     $0002 - Setup failed
*   Carry flag set means you can stop calling me
SMB_ReadFile_Poll
				plx							; our return address
				PullLong SMB_sessid			; your smb sessid
				phx

				_TCPIPPoll

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushLong #statbuf
				_TCPIPStatusTCP				; see if marinetti has anything for us
				pla
				cmp #terrNOCONNECTION
				beq rf_trampoline
				cmp #terrBADIPID
				beq rf_trampoline
				lda statbuf+8				; get recvq size, low word
				cmp #0000					; yeah i know. for clarity.
				beq rp_trampoline			; poll us again later, marinetti got nothing

				PushWord #0000				; space for result
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha							; push Marinetti IPID for this SMB_sessid
				PushWord #0000				; bufftype: static pre-allocated buffer
				PushLong #SMB_input			; where it's all goin
				PushLong #SMB_max_net_read_size
				PushLong #readbuf
				_TCPIPReadTCP

				pla
				cmp #terrNOCONNECTION
				beq rf_trampoline
				cmp #terrBADIPID
				beq rf_trampoline
								
				jsr _SMB_Check				; do basic check to make sure we received SMB data
				bcs rp_trampoline			; if not, wait for them to send again i guess

				bra rft_far
rf_trampoline	jmp readx_failed
rp_trampoline	jmp readx_proceeding
rft_far
				lda SMB_input+SMB_offset_cmd
				cmp #SMB_read_ANDX
				bne rp_trampoline			; punt if not read_ANDX reply
				
				lda SMB_input+SMB_offset_eclass
				cmp #0000
				bne rf_trampoline			; they returned an error, kbye
				
; TODO something

readx_finished	plx							; our return address
				PushWord #0001				; finished!
				phx
				sec
				rts

readx_failed	plx							; our return address
				PushWord #0002				; failure
				phx
				sec
				rts

readx_proceeding
				plx							; our return address
				PushWord #0000				; in progress
				phx
				clc
				rts

*
* SMB_WriteFile - Write to an open file from the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to data buffer (two words, on stack)
*    Size of data buffer (two words, on stack)
*    Offset into file (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_WriteFile	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_write_ANDX	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_PathInfo - Get information about a file/path on the remote share
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to pathname (two words, on stack)
*    Long pointer to smb direntry struct buffer (two words, on stack)
* Things I return on stack:
*    Carry flag set if error
SMB_PathInfo	plx							; return address
				PullLong SMB_sessid
				phx							; saved return address

				PushWord #SMB_query_path_info	; command
				PushWord #CIFS_flags1		; flags1
				PushWord #CIFS_flags2		; flags2
				jsr _InitSMBHeader			; make an SMB header with this information

* TODO

				rts

*
* SMB_FindFirst - Find first file in a directory listing
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to pathname (two words, on stack)
*    Flags (one word, on stack)
*    Long pointer to smb direntry struct buffer (two words, on stack)
* Things I return on stack:
*    Carry flag set if error

* TODO TRANS2 call

*
* SMB_FindNext - Find next file in a directory listing
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to smb direntry struct buffer (two words, on stack)
* Things I return on stack:
*    Carry flag set if error

* TODO TRANS2 call

*
* SMB_FindClose - Close a directory listing
* Arguments:
*    SMB session handle (two words, on stack)
*    Long pointer to smb direntry struct buffer (two words, on stack)
* Things I return on stack:
*    Carry flag set if error

* TODO TRANS2 call


*
* Internal subroutines for SMB stuff
*

* _strcpy - Copy a string somewhere...
* Arguments:
*    Pointer to source (two words, on stack)
*    Pointer to destination (two words, on stack)
* returns:
*    Y = number of bytes copied (including terminating zero)
_strcpy			plx
				PullLong SMB_tmp1	; Destination
				PullLong SMB_tmp3	; Source
				phx
				ldy #00
				sep #$30
				mx %11
_sclp			lda [SMB_tmp3],y
				sta [SMB_tmp1],y
				cmp #00
				beq _scrt
				iny
				bra _sclp
_scrt			rep #$30
				mx %00
				iny
				rts				

* _strncpy - Copy a string somewhere, only n bytes
* Arguments:
*    Pointer to source (two words, on stack)
*    Pointer to destination (two words, on stack)
*    X = number of bytes to copy
_strncpy		txy
				plx
				PullLong SMB_tmp1	; Destination
				PullLong SMB_tmp3	; Source
				phx
				tyx
				ldy #00
				sep #$30
				mx %11
_snclp			lda [SMB_tmp3],y
				sta [SMB_tmp1],y
				dex
				cpx #00
				beq _sncrt
				iny
				bra _snclp
_sncrt			rep #$30
				mx %00
				rts	

* _SMB_Check - Check to see if TCP received data is SMB
_SMB_Check		lda SMB_input+SMB_offset_proto
				cmp #SMB_proto1
				bne check_inv
				lda SMB_input+SMB_offset_proto+2
				cmp #SMB_proto2
				bne check_inv					; starts with 'SMB'\ff
				clc
				rts
check_inv		sec
				rts

* _SMB_Send - Send the SMB datagram staging area out to Marinetti
* Arguments:
*    length  (word, pushed on stack first)
_SMB_Send		plx								; save return address
				PullWord SMB_tmp1
				phx								; restore return address
				lda #0
				sta SMB_tmp2
				PushWord #0000
				ldy #SMB_sess_ipid-SMB_sess_begin
				lda [SMB_sessid],y
				pha								; IPID for Marinetti
				PushLong #SMB_staging
				PushLong SMB_tmp1				; length
				PushWord #0001					; Samba sets PSH, so will I
				PushWord #0000
				_TCPIPWriteTCP
				pla
				rts

* _InitSMBHeader - Zero-out old SMB datagram data and start with some fresh header values
* Arguments:
*    Command (word, pushed on stack first)
*    Flags1  (word, pushed on stack second)
*    Flags2  (word, pushed on stack third)
*    Session Handle (already stored in DP SMB_sessid)
* Things I return on stack:
*    Carry flag set if error
_InitSMBHeader	ldx #00
				lda #00
zero_loop		sta SMB_staging,x				; loop to zero-out any old data from SMB header staging place
				inx
				inx
				cpx #SMB_max_transmit_size
				blt zero_loop

				lda #SMB_proto1
				sta SMB_staging+SMB_offset_proto
				lda #SMB_proto2
				sta SMB_staging+SMB_offset_proto+2	; $FF + 'SMB' at start of header
				
				ldy #SMB_sess_tid-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_offset_tid		; set session TID

				ldy #SMB_sess_pid-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_offset_pid		; set session PID
				
				ldy #SMB_sess_uid-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_offset_uid		; set session UID
				
				ldy #SMB_sess_mid-SMB_sess_begin
				lda [SMB_sessid],y
				sta SMB_staging+SMB_offset_mid		; set session MID
				
				plx									; save return address
				pla									; flags2 (word)
				sta SMB_staging+SMB_offset_flags2	; set Flags2
				
				pla									; flags1 (word, but written as byte)
				ora SMB_staging+SMB_offset_flags
				sta SMB_staging+SMB_offset_flags	; write one-byte value where it goes
				
				pla									; command (word, but written as byte)
				ora SMB_staging+SMB_offset_cmd
				sta SMB_staging+SMB_offset_cmd		; write one-byte value where it goes
				
				phx
				rts
