1414 lines
30 KiB
ArmAsm
1414 lines
30 KiB
ArmAsm
*
|
|
* Marinetti / SMB2 Test, 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
|
|
*
|
|
* 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 #$800 ; 8 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
|
|
_SoundStartUp
|
|
jsr CheckError
|
|
|
|
_TCPIPStartUp
|
|
jsr CheckError
|
|
|
|
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
|
|
|
|
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
|
|
|
|
noevent3 PushLong MySMBHandle
|
|
jsr SMB_Negotiate_Poll
|
|
pla ; get negotiation status
|
|
bcc sendloop2
|
|
|
|
login PushLong CTSWinPtr
|
|
PushWord #1350
|
|
PushLong #CTSTextB
|
|
_SetIText
|
|
|
|
jmp SMB_input_brk ; die so we can inspect things
|
|
|
|
closed PushLong CTSWinPtr
|
|
PushWord #1350
|
|
PushLong #CTSTextA
|
|
_SetIText
|
|
|
|
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 13 ; 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
|
|
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'
|
|
|
|
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 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 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 stock)
|
|
* 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
|
|
sta [SMB_sessid],y
|
|
|
|
lda SMB_input+SMB_header_size+36
|
|
ldy #SMB_sess_challenge-SMB_sess_begin
|
|
sta [SMB_sessid],y
|
|
iny
|
|
iny
|
|
lda SMB_input+SMB_header_size+38
|
|
sta [SMB_sessid],y
|
|
iny
|
|
iny
|
|
lda SMB_input+SMB_header_size+40
|
|
sta [SMB_sessid],y
|
|
iny
|
|
iny
|
|
lda SMB_input+SMB_header_size+42
|
|
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 #$ff00
|
|
xba
|
|
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 internal subroutines
|
|
*
|
|
|
|
* _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
|