;--- FTP 1.0 - FTP client for the TCP/IP UNAPI
; By Konami Man, 4/2010
.label 20
;******************************
;*** ***
;*** MACROS, CONSTANTES ***
;*** ***
;******************************
nesman: macro @f ;Llamada indirecta a las funciones de NestorMan
ld c,@f
ld de,#2202
call #FFCA
endm
print: macro @d ;Imprime una cadena acabada en "$"
ld de,@d
ld c,_STROUT
call DO_DOS
endm
printl: macro @d ;Imprime una cadena acabada en CRLF
ld de,@d
call PRINT_L
endm
printz: macro @d ;Imprime una cadena acabada en cero
ld de,@d
call PRINT_Z
endm
_TERM0: equ #00
_CONOUT: equ #02 ;Some DOS function calls
_DIRIO: equ #06
_INNOE: equ #08
_STROUT: equ #09
_BUFIN: equ #0A
_CONST: equ #0B
_SELDSK: equ #0E
_CURDRV: equ #19
_ALLOC: equ #1B
_FFIRST: equ #40
_FNEXT: equ #41
_OPEN: equ #43
_CREATE: equ #44
_CLOSE: equ #45
_READ: equ #48
_WRITE: equ #49
_DELETE: equ #4D
_RENAME: equ #4E
_ATTR: equ #50
_GETCD: equ #59
_CHDIR: equ #5A
_PARSE: equ #5B
_TERM: equ #62
_DEFAB: equ #63
_ERROR: equ #65
_EXPLAIN: equ #66
_GENV: equ #6B
_DOSVER: equ #6F
ENASLT: equ #0024 ;Slot swithcing BIOS routine
BEEP: equ #00C0 ;Generates a BEEP
CALSLT: equ #001C ;Calls a routine on another slot
TPASLOT1: equ #F342
ARG: equ #F847
H_CHPH: equ #FDA4 ;Gancho CHPUT
EXTBIO: equ #FFCA
;--- TCP/IP UNAPI routines
TCPIP_GET_CAPAB: equ 1
TCPIP_GET_IPINFO: equ 2
TCPIP_NET_STATE: equ 3
TCPIP_DNS_Q: equ 6
TCPIP_DNS_S: equ 7
TCPIP_TCP_OPEN: equ 13
TCPIP_TCP_CLOSE: equ 14
TCPIP_TCP_ABORT: equ 15
TCPIP_TCP_STATE: equ 16
TCPIP_TCP_SEND: equ 17
TCPIP_TCP_RCV: equ 18
TCPIP_WAIT: equ 29
;--- TCP/IP UNAPI error codes
ERR_OK: equ 0
ERR_NOT_IMP: equ 1
ERR_NO_NETWORK: equ 2
ERR_NO_DATA: equ 3
ERR_INV_PARAM: equ 4
ERR_QUERY_EXISTS: equ 5
ERR_INV_IP: equ 6
ERR_NO_DNS: equ 7
ERR_DNS: equ 8
ERR_NO_FREE_CONN: equ 9
ERR_CONN_EXISTS: equ 10
ERR_NO_CONN: equ 11
ERR_CONN_STATE: equ 12
ERR_BUFFER: equ 13
ERR_LARGE_DGRAM: equ 14
ERR_INV_OPER: equ 15
debug: macro @c
push af,bc,de,hl
ld e,@c
ld c,2
call DO_DOS
pop hl,de,bc,af
endm
; Conditional relative jumps
jri: macro @a ;A = x
jr z,@a
endm
jrni: macro @a ;A <> x
jr nz,@a
endm
jrmn: macro @a ;A < x
jr c,@a
endm
jrmy: macro @a ;A > x
jr z,$+4
jr nc,@a
endm
jrmni: macro @a ;A <= x
jr c,@a
jr z,@a
endm
jrmyi: macro @a ;A >= x
jr nc,@a
endm
; Conditional absolute jumps
jpi: macro @a ;A = x
jp z,@a
endm
jpni: macro @a ;A <> x
jp nz,@a
endm
jpmn: macro @a ;A < x
jp c,@a
endm
jpmy: macro @a ;A > x
jr z,$+5
jp nc,@a
endm
jpmni: macro @a ;A <= x
jp c,@a
jp z,@a
endm
jpmyi: macro @a ;A >=x
jp nc,@a
endm
;**************************
;*** ***
;*** INICIALIZACION ***
;*** ***
;**************************
org #100
ld hl,DATA
ld de,#8000
ld bc,DATA_END-DATA_START
ldir
ld a,#FF
ld (USER_COM_BUF),a
;--- Muestra la cadena inicial
print PRESEN_S ;Presentation string
ld (SAVESP),sp
;--- Comprueba la version del DOS
ld c,_DOSVER
call DO_DOS
or a
jr nz,NODOS2
ld a,b
cp 2
jr z,OKDOS2
NODOS2: print NODOS2_S
ld c,_TERM0
jp DO_DOS
NODOS2_S: db "*** Sorry, this program is for DOS 2 only$"
OKDOS2:
;--- Search the TCP/IP UNAPI implementation
ld hl,TCPIP_S
ld de,ARG
ld bc,15
ldir
xor a
ld b,0
ld de,#2222
call EXTBIO
ld a,b
or a
jr nz,OKINS
print NOTCPIP_S
ld c,_TERM0
jp DO_DOS
TCPIP_S: db "TCP/IP",0
NOTCPIP_S: db "*** No TCP/IP UNAPI implementation found.",13,10,"$"
OKINS: ;
;--- Setup the UNAPI calling code
ld a,1
ld de,#2222
call EXTBIO
ld (DO_UNAPI+1),hl
ld c,a
ld a,h
cp #C0
ld a,c
jr c,NO_UNAPI_P3
ld a,#C9
ld (SET_UNAPI),a
jr OK_SET_UNAPI
NO_UNAPI_P3:
ld (UNAPI_SLOT+1),a
ld a,b
cp #FF
jr nz,NO_UNAPI_ROM
ld a,#C9
ld (UNAPI_SEG),a
jr OK_SET_UNAPI
NO_UNAPI_ROM:
ld (UNAPI_SEG+1),a
OK_SET_UNAPI:
;--- Get mapper support routines (we need GET_P1 and PUT_P1)
ld de,#0402
xor a
call EXTBIO
or a
jr z,NOMAPPER
ld bc,10*3 ;Skip ALL_SEG to GET_P0
add hl,bc
ld de,PUT_P1
ld bc,2*3
ldir
call GET_P1
ld (TPASEG1),a
NOMAPPER:
;--- Comprueba que realmente estemos conectados,
; en caso contrario muestra error y termina
ld a,TCPIP_NET_STATE ;La red esta disponible?
call CALL_UNAPI
or a
jr z,CONNECT_OK ;Assume ok if "unknown"
cp 2
jr z,CONNECT_OK
print NOCON_S
jp TERM
NOCON_S: db "*** No network connection",13,10,"$"
CONNECT_OK: ;
;--- Check if this implementation can open active and passive TCP connections
ld b,1
ld a,TCPIP_GET_CAPAB
call CALL_UNAPI
bit 3,l
jr nz,CAN_ACTIVE
print NOTCPA_S
jp TERM
NOTCPA_S: db "*** This TCP/IP UNAPI implementation does not support",13,10
db " opening active TCP connections.",13,10,"$"
CAN_ACTIVE:
bit 5,l
jp nz,CAN_PASSIVE
print NOTCPPU_S
jp TERM
NOTCPPU_S: db "*** This TCP/IP UNAPI implementation does not support",13,10
db " opening passive TCP connections with remote socket unespecified.",13,10,"$"
CAN_PASSIVE:
;--- Clear all transient connections
ld a,TCPIP_TCP_CLOSE
ld b,0
call CALL_UNAPI
;--- Comprueba que haya al menos dos conexiones TCP libres
ld b,2
ld a,TCPIP_GET_CAPAB
call CALL_UNAPI
ld a,d
cp 2
jr nc,OKFRCON
print NOFRCON_S
jp TERM
NOFRCON_S: db "*** Not enough free TCP connections available (I need two)$"
OKFRCON:
;--- Comprueba que NestorMan 1.21 o superior este instalado,
; y establece HAY_NMAN en ese caso
xor a ;Installed?
nesman 1
or a
jr z,OKNMAN
OKNMAN1: ld hl,#0201 ;Installed: now check version
ex de,hl
call COMP
jr nc,OKNMAN
ld a,#FF
ld (HAY_NMAN),a
OKNMAN:
;--- Obtiene la IP local.
; Con esta informacion compone la primera parte
; de la cadena que forma el comando "PORT".
ld a,TCPIP_GET_IPINFO
ld b,1
call CALL_UNAPI
ld (IP_LOCAL),hl
ld (IP_LOCAL+2),de
ld a,l
call SET_IP
ld a,h
call SET_IP
ld a,e
call SET_IP
ld a,d
call SET_IP
jr OK_SET_PORT
SET_IP: push hl,de
ld e,a
ld d,0
ld b,1
xor a
ld hl,(PORT_C_PNT)
call NUMTOASC
ld c,b
ld b,0
add hl,bc
ld (hl),","
inc hl
ld (PORT_C_PNT),hl
pop de,hl
ret
OK_SET_PORT: ;
;--- Compone DEF_USER y DEF_PASSWORD
ld hl,ENV_USER
ld de,DEF_USER
ld b,255
ld c,_GENV
call DO_DOS
ld hl,ENV_PASSWORD
ld de,DEF_PASSWORD
ld b,255
ld c,_GENV
call DO_DOS
ld hl,ENV_ACCOUNT
ld de,DEF_ACCOUNT
ld b,255
ld c,_GENV
call DO_DOS
ld a,(DEF_USER)
or a
jr nz,OK_DEFUSER
ld hl,ANONYMOUS_S
ld de,DEF_USER ;Por si no hay variable FTP_USER
ld bc,10
ldir
OK_DEFUSER: ;
;--- Copia el codigo del gancho CHPUT
ld hl,HOOK_CODE0
ld de,HOOK_CODE
ld bc,HOOK_CODE0_END-HOOK_CODE0
ldir
ld hl,H_CHPH
ld de,HOOK_OLD
ld bc,5
ldir
xor a
ld (ESC_CHAR),a
;--- Define la rutina de terminacion
ld de,R_ABORT
ld c,_DEFAB
call DO_DOS
;--- Si hay parametros, salta a OPEN
ld hl,#80
ld de,USER_COM_BUF+1
ld a,1
call EXTPAR
jr c,NOPARS
ld hl,#81 ;Copia el comando ficticio "X"
ld de,USER_COM_BUF+3
ld bc,255 ;y los parametros a USER_COM_BUF,
ldir
ld a,(#80) ;entonces salta a OPEN
inc a
inc a
ld (USER_COM_BUF+1),a
ld hl," X"
ld (USER_COM_BUF+2),hl
call R_OPEN
NOPARS: ;
;***************************
;*** ***
;*** BUCLE PRINCIPAL ***
;*** ***
;***************************
MAIN_LOOP: ld sp,(SAVESP)
;--- Pone ABORT_STAT a 1 o a 2
ld a,(CONTROL_CON)
cp #FF
ld a,1
jr z,MAINLOP_2
inc a
MAINLOP_2: ld (ABORT_STAT),a
;--- Imprime prompt y queda a la espera de un comando
xor a
ld (ES_XCOM),a
print FTP_S
ld de,USER_COM_BUF
ld c,_BUFIN
call DO_DOS
call LF
ld a,(USER_COM_BUF+1) ;Anyade 0 al final
ld c,a
ld b,0
ld hl,USER_COM_BUF+2
add hl,bc
ld (hl),0
;--- Busca en la tabla el comando adecuado,
; y salta a su rutina.
; Si no existe, muestra "Unknown command".
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF
ld a,1
call EXTPAR
jr c,MAIN_LOOP
ld a,(PARSE_BUF)
cp "!"
jr nz,NOADMIRA
ld hl,R_LITERAL!
jr COM_OK
NOADMIRA: ld hl,PARSE_BUF
call SEARCH_COM
jr nc,COM_OK
print UNKCOM_S
jr MAIN_LOOP
COM_OK: ld a,(CONTROL_CON) ;Flushea datos si hay conexion
cp #FF
jr z,COM_OK2
;call TCP_STATUS
;jr c,COM_OK2
;cp 4
;jr nz,COM_OK2
push hl
call R_FLUSH
pop hl
COM_OK2: call JP_HL ;Ejecuta comando
ld a,(BELL)
or a
jr z,MAIN_LOOP
ld iy,(#FCC1-1) ;Ejecuta BEEP si BELL=#FF
ld ix,BEEP
call CALSLT
jp MAIN_LOOP
;******************************
;*** ***
;*** COMANDOS DE USUARIO ***
;*** ***
;******************************
;-----------------
;--- ?, HELP ---
;-----------------
R_HELP: ld a,3
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF
ld a,2
call EXTPAR
jr nc,COMM_HELP
print BIGHELP_H ;Solo "HELP": Muestra ayuda general
ret
COMM_HELP: cp 2
jp nz,R_INVPAR ;Mas de un parametro: error
COMM_HELP2: ld hl,PARSE_BUF
call SEARCH_COM
jr nc,COMM_HELP3
ld e,34
ld c,_CONOUT
call DO_DOS
printz PARSE_BUF
ld e,34
ld c,_CONOUT
call DO_DOS
print NOHELP_S ;No existe ese comando: error
ret
COMM_HELP3: push de
call LF
pop de
ld c,_STROUT
call DO_DOS
jp LF
;----------------
;--- APPEND ---
;----------------
R_APPEND: ld a,3
ld hl,C_APPE
jp R_SEND_APP
;---------------
;--- ASCII ---
;---------------
R_ASCII: ld a,4
ld (ABORT_STAT),a
xor a
push af
ld a,"A"
R_ASCBIN: ld (SEND_COM_BUF+5),a
ld hl,#0A0D ;Vuelve con Cy=1 en caso de error
ld (SEND_COM_BUF+6),hl ;(usado por R_DIR)
ld hl,C_TYPE
call SET_COMMAND
call SET_SPACE
call AUTOMATA_NO1
pop bc
ret c
ld a,b
ld (TYPE),a
or a
ret
;--------------
;--- BELL ---
;--------------
R_BELL: ld a,3
ld (ABORT_STAT),a
ld hl,BELL
ld de,BELL_M_S
jp TURN_ON_OFF
;----------------
;--- BINARY ---
;----------------
R_BINARY: ;ld a,4
;ld (ABORT_STAT),a
ld a,#FF
push af
ld a,"I"
jp R_ASCBIN
;------------
;--- CD ---
;------------
R_CD: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+4
ld a,2
call EXTPAR
jp c,R_PWD
ld a,#FF
ld (ES_XCOM),a
ld hl,C_CWD
jp PARAM_XCOM
;--------------
;--- CDUP ---
;--------------
R_CDUP: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld hl,C_CDUP
ld a,(XCOM)
or a
jp z,SINGLE_COM
ld hl,C_XCUP
jp SINGLE_COM
;---------------
;--- CLOSE ---
;---------------
R_CLOSE: call CHK_CON
ret c
ld a,5
ld (ABORT_STAT),a
ld a,#FF
ld (QUITTING),a
ld hl,C_QUIT
call SET_COMMAND
call APPEND_LF
call AUTOMATA_NO1
ld a,(CONTROL_CON)
ld b,a
ld a,TCPIP_TCP_CLOSE
call CALL_UNAPI
ld a,#FF
ld (CONTROL_CON),a
xor a
ld (QUITTING),a
ld (GCDATA_COUNT),a
ld (TYPE),a
jp MAIN_LOOP
;---------------
;--- DEBUG ---
;---------------
R_DEBUG: ld a,3
ld (ABORT_STAT),a
ld hl,DEBUG
ld de,DEBUG_M_S
jp TURN_ON_OFF
;----------------
;--- DELETE ---
;----------------
R_DELETE: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
ld a,2
call EXTPAR
ld hl,C_DELE
jp nc,PARAM_COM
jp R_INVPAR
;-------------
;--- DIR ---
;-------------
R_DIR: ld hl,C_LIST
R_DIR_LS: ld (RETR_COM),hl
ld a,4
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1 ;Busca tercer parametro
ld de,DATA_BUF ;(nombre de fichero)
ld a,3
call EXTPAR
jr c,R_DIR_LS2
call CLOSE_FILE ;Crea el fichero
ld c,_CREATE
ld de,DATA_BUF
xor a
ld b,0
call DOS
ret c
ld a,b
ld (FILE_FH),a
R_DIR_LS2: ld a,2 ;Ejecuta comando
ld (RETR_PAR),a
ld a,#FF
ld (MUST_ASCII),a
jp RETRIEVE
;---------------
;--- FLUSH --- (ejecutado por el bucle principal)
;---------------
R_FLUSH: ld a,3
ld (ABORT_STAT),a
;call CHK_CON
;ret c
R_FLUSH2:
ld a,(CONTROL_CON) ;Hay datos sin recoger?
ld b,a
ld hl,0
ld a,TCPIP_TCP_STATE
call CALL_UNAPI
or a
ret z
ld a,h
or l
jr nz,R_FLUSH2!
ld a,(GCDATA_COUNT) ;Hay datos recogidos pero no consumidos?
or a
ret z
R_FLUSH2!: print FLUSHED_S
R_FLUSH3: call GET_CDATA
jr c,R_FLUSH_END
push af
ld e,a
ld c,_CONOUT
call DO_DOS
pop af
cp 10
jr z,R_FLUSH2
jr R_FLUSH3
R_FLUSH_END: call CRLF
ret
;-------------
;--- GET ---
;-------------
R_GET: ld a,4
ld (ABORT_STAT),a
ld hl,C_RETR ;Coge el fichero
ld (RETR_COM),hl
ld hl,USER_COM_BUF+1 ;Error si no hay parametros
ld de,PARSE_BUF
ld a,2
call EXTPAR
jp c,R_INVPAR
call CLOSE_FILE ;Crea el fichero
ld c,_CREATE
ld de,PARSE_BUF
xor a
ld b,0
call DOS
ret c
ld a,b
ld (FILE_FH),a
ld hl,C_RETR ;Coge el fichero
ld (RETR_COM),hl
ld a,2
ld (RETR_PAR),a
xor a
ld (MUST_ASCII),a
jp RETRIEVE
;--------------
;--- HASH ---
;--------------
R_HASH: ld a,3
ld (ABORT_STAT),a
ld hl,HASH
ld de,HASH_M_S
jp TURN_ON_OFF
;-------------
;--- LCD ---
;-------------
R_LCD: ld a,3
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,RESPONSE_BUF
ld a,2
call EXTPAR
jr nc,LCD_SET
;--- No se ha especificado parametro:
; muestra unidad+dir actual
print CURPATH_S
jp PRINT_PATH
;--- Se ha especificado parametro:
; Establece unidad y/o directorio
LCD_SET: ld c,_PARSE
ld b,0
ld de,RESPONSE_BUF
call DO_DOS
bit 2,b
ld de,RESPONSE_BUF
ld h,b
jr z,LCD_NODRIVE
push bc
ld e,c ;Establece unidad
dec e
ld c,_SELDSK
call DO_DOS
ld c,_ERROR
call DO_DOS
pop hl
ld a,b
or a
jp nz,DOS2
ld de,RESPONSE_BUF+2
LCD_NODRIVE: ;
bit 0,h
jr z,LCD_NOPATH
ld c,_CHDIR ;Establece directorio
call DOS
ret c
LCD_END: print PATHCHAN_S
jp PRINT_PATH
LCD_NOPATH: ld a,"\" ;Si se especifica unidad pero no dir,
ld (RESPONSE_BUF+2),a ;se muestra el dir actual
ld c,_GETCD
ld de,RESPONSE_BUF+3
ld c,_GETCD
call DO_DOS
jr LCD_END
;-----------------
;--- LDELETE ---
;-----------------
R_LDELETE: ;ld a,#FF
;ld (LDIR_EXE),a
call _R_LDELETE
xor a
ld (LDIR_EXE),a
ret
_R_LDELETE: ld a,4
ld (ABORT_STAT),a
xor a
ld (ALSO_RONLY),a
ld (BORRALGUNO),a
ld a,(PROMPT)
cpl
ld (ALLFILES),a
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF ;Extrae nombre(s)
ld a,2
call EXTPAR
jp c,R_INVPAR
ld hl,USER_COM_BUF+1 ;Busca parametro "R",
ld de,RESPONSE_BUF ;si lo encuentra establece ALSO_RONLY
ld a,3
call EXTPAR
jr c,RLDEL2
ld a,(RESPONSE_BUF+1)
or a
jp nz,R_INVPAR
ld a,(RESPONSE_BUF)
or %00100000
cp "r"
jp nz,R_INVPAR
ld a,#FF
ld (ALSO_RONLY),a
RLDEL2: ;
;--- Busca el primer (o siguiente) fichero disponible
ld c,_FFIRST
ld de,PARSE_BUF
ld b,%00111 ;Incluye ficheros ocultos y de sistema
R_LDELLOOP: ld ix,USER_COM_BUF+1
call DOS ;Realiza la busqueda
jr c,R_LDELEND
ld a,#FF ;Solo imprimira "file not found"
ld (LDIR_EXE),a ;si no encuentra el primero
;--- Comprueba si es de solo lectura,
; en ese caso se lo salta si ALSO_RONLY=0
ld a,(USER_COM_BUF+15)
bit 0,a
jr z,LDEL_NORO
ld a,(ALSO_RONLY)
or a
jr z,LDEL_NODEL
LDEL_NORO: ;
;--- Si ALLFILES=#FF, pregunta
; si se quiere borrar; si no, lo borra directamente
ld a,(ALLFILES)
or a
jr nz,LDEL_DEL
print DELETE_S
printz USER_COM_BUF+2
call ASK_YNAC
or a
jr z,LDEL_DEL
cp 1
jr z,LDEL_NODEL
cp 3
jr z,LDEL_CANCEL
ld a,#FF
ld (ALLFILES),a
;--- Borrar fichero: primero le quita
; el atributo de solo lectura si lo tiene
LDEL_DEL: ld a,(USER_COM_BUF+15)
bit 0,a
jr z,LDEL_DEL2
ld l,a
res 0,l
ld c,_ATTR
ld de,USER_COM_BUF+1 ;Quita el atributo "Read only"
ld a,1
call DOS
ret c
LDEL_DEL2: ld de,USER_COM_BUF+1 ;Borra el fichero
ld c,_DELETE
call DOS
ret c
ld a,#FF
ld (BORRALGUNO),a
;--- Pasa al siguiente fichero
LDEL_NODEL: ld c,_FNEXT
jp R_LDELLOOP
;--- Proceso completado
R_LDELEND: ld a,(LDIR_EXE)
or a
ret z
ld a,(BORRALGUNO)
or a
ret z
print OPCOMP_S
ret
;--- Finalizacion
LDEL_CANCEL: print OPCAN_S
ret
BORRALGUNO: db 0 ;Al final sera 0 si no se ha borrado nada
;--------------
;--- LDIR ---
;--------------
R_LDIR: ld a,#FF
ld (LDIR_EXE),a
call _R_LDIR
xor a
ld (LDIR_EXE),a
ret
_R_LDIR: ld a,3
ld (ABORT_STAT),a
xor a
ld (RESPONSE_BUF),a ;Extrae mascara
ld hl,USER_COM_BUF+1
ld de,RESPONSE_BUF
ld a,2
call EXTPAR
ld de,RESPONSE_BUF ;Obtiene unidad a la que se refiere la mascara
ld b,0
ld c,_PARSE
call DO_DOS
ld a,c
ld (LDIR_DRV),a
;print DIROF_S ;Muestra "Directory of "+ruta
;call PRINT_PATH
call CRLF
ld hl,0
ld (LDIR_FILES),hl
ld (LDIR_DIRS),hl
ld c,_FFIRST
ld de,RESPONSE_BUF
ld b,%10111 ;Incluye directorios, ocultos y de sistema
R_LDIRLOOP: ld ix,USER_COM_BUF+1
call DOS ;Realiza la busqueda
jp c,LDIR_END
ld hl,USER_COM_BUF+2-1 ;Ajusta nombre de fichero
ld b,12 ;para que ocupe exatamente 12 caracteres
R_LDIRLOP2: inc hl ;(rellena con espacios)
ld a,(hl)
or a
jr z,R_LDIRLOP3
djnz R_LDIRLOP2
jr R_LDIRLOP4
R_LDIRLOP3: ld (hl)," "
inc hl
djnz R_LDIRLOP3
R_LDIRLOP4: xor a
ld (USER_COM_BUF+2+12),a
printz USER_COM_BUF+2
ld a," "
ld (ATRIB_S+1),a
ld (ATRIB_S+2),a
ld (ATRIB_S+3),a
ld a,(USER_COM_BUF+15) ;Imprime atributos
bit 0,a
jr z,LDIR_AT1
push af
ld a,"r" ;Read only
ld (ATRIB_S+1),a
pop af
LDIR_AT1: bit 1,a
jr z,LDIR_AT2
push af
ld a,"h" ;Hidden
ld (ATRIB_S+2),a
pop af
LDIR_AT2: bit 2,a
jr z,LDIR_AT3
push af
ld a,"s" ;System
ld (ATRIB_S+3),a
pop af
LDIR_AT3: push af
print ATRIB_S
pop af
bit 4,a
jr z,LDIR_ESFILE
LDIR_ESDIR: ;Es directorio? Imprime "
"
print ESDIR_S
ld hl,(LDIR_DIRS)
inc hl
ld (LDIR_DIRS),hl
jr LDIR_OKFILE
LDIR_ESFILE: ld hl,(LDIR_FILES) ;Es fichero? Imprime tamanyo
inc hl
ld (LDIR_FILES),hl
ld ix,USER_COM_BUF+22 ;IX=Tamanyo del fichero
ld a,(ix+2)
or (ix+3)
ld e,(ix)
ld d,(ix+1)
jr z,LDIR_PEQUEN ;"Grande" si es mayor de 64K
;ld a,(ix+1)
;and %11000000
;ld e,(ix)
;ld d,(ix+1)
;jr z,LDIR_PEQUEN
LDIR_GRANDE: ld a,(ix+3) ;A-DE = Tamanyo en unidades de 256 bytes
ld e,(ix+1)
ld d,(ix+2)
sra a ;Lo convierte a K
rr d
rr e
sra a
rr d
rr e
inc de
scf
LDIR_PEQUEN: ;Aqui, DE es el tamanyo a imprimir, y Cy=1 si es "K"
ld hl,RESPONSE_BUF
ld b,7
ld c," "
push af
ld a,%1000
call NUMTOASC
print RESPONSE_BUF
pop af
jr nc,LDIR_OKFILE
ld e,"K"
ld c,_CONOUT
call DO_DOS
LDIR_OKFILE: call CRLF
ld c,_FNEXT
jp R_LDIRLOOP
LDIR_END: ld (LDIR_ERRNUM),a
ld hl,(LDIR_DIRS)
ld de,(LDIR_FILES)
ld a,d
or e
or h
or l
call nz,CRLF
ld hl,RESPONSE_BUF ;Fin: muestra num de dirs y files encontrados
ld de,(LDIR_FILES)
ld b,1
ld a,%1000
call NUMTOASC
print RESPONSE_BUF
print FFOUND_S
ld hl,RESPONSE_BUF
ld de,(LDIR_DIRS)
ld b,1
ld a,%1000
call NUMTOASC
print RESPONSE_BUF
print DFOUND_S
ld a,(LDIR_ERRNUM) ;Si el error no era "file not found",
cp #D7 ;no calcula espacio libre
jr nz,LDIR_ENDL3
ld a,(LDIR_DRV) ;Consulta espacio libre en la unidad
ld e,a
ld c,_ALLOC
call DO_DOS
LDIR_ENDL: sra a ;Convierte clusters a sectores
jr c,LDIR_ENDL2
sla l
rl h
jr nc,LDIR_ENDL
LDIR_ENDL2: srl h ;Convierte sectores a K
rr l
ex de,hl ;Muestra "XXX K free on drive X:"
ld hl,RESPONSE_BUF
ld b,1
ld a,%1000
call NUMTOASC
print RESPONSE_BUF
print KFREE_S
ld a,(LDIR_DRV)
add "A"-1
ld e,a
ld c,_CONOUT
call DO_DOS
ld e,":"
ld c,_CONOUT
call DO_DOS
call CRLF
LDIR_ENDL3: jp LF
LDIR_EXE: db 0
LDIR_FILES: dw 0
LDIR_DIRS: dw 0
LDIR_DRV: db 0
ATRIB_S: db 9,"rhs",9,"$"
LDIR_ERRNUM: db 0
;-----------------
;--- LITERAL ---
;-----------------
R_LITERAL: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld a,2 ;Va cogiendo los elementos de la cadena
;ld hl,USER_COM_BUF+1 ;pasada por el usuario, y los
ld de,SEND_COM_BUF ;copia a SEND_COM_BUF
LITELOOP: push af,de ;separados con un espacio
ld hl,USER_COM_BUF+1
call EXTPAR
pop hl
jr c,LITERAL2
ld c,b
ld b,0
add hl,bc
ld (hl)," "
inc hl
ex de,hl
pop af
inc a
jr LITELOOP
LITERAL2: pop af
dec hl
ld (hl),0
call APPEND_LF
call SEND_COM
jp WAIT_REPLY
;-----------
;--- ! ---
;-----------
R_LITERAL!: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld a,1 ;Como LITERAL, pero empieza por el primer
;ld hl,USER_COM_BUF+1 ;elemento de la cadena
ld de,SEND_COM_BUF-1 ;excepto el "!" inicial
jr LITELOOP
;----------------
;--- LMKDIR ---
;----------------
R_LMKDIR: ld a,3
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF
ld a,2
call EXTPAR
jp c,R_INVPAR
ld c,_CREATE
ld de,PARSE_BUF
xor a
ld b,%10000
call DOS
ret c
print OPCOMP_S
ret
;-----------------
;--- LRENAME ---
;-----------------
R_LRENAME: ;ld a,#FF
;ld (LDIR_EXE),a
call _R_LRENAME
xor a
ld (LDIR_EXE),a
ret
_R_LRENAME: ld a,3
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,RESPONSE_BUF ;Extrae nombre(s) de destino
ld a,3
call EXTPAR
jp c,R_INVPAR
ld hl,USER_COM_BUF+1 ;Extrae nombre(s) de origen
ld de,PARSE_BUF
ld a,2
call EXTPAR
ld c,_FFIRST
ld de,PARSE_BUF
ld b,%00111 ;Incluye ficheros ocultos y de sistema
R_LRENLOOP: ld ix,USER_COM_BUF+1
call DOS ;Realiza la busqueda
jr c,R_LRENEND
ld a,#FF ;Solo imprimira "file not found"
ld (LDIR_EXE),a ;si no encuentra el primero
ld de,USER_COM_BUF+1 ;Renombra cada fichero encontrado
ld hl,RESPONSE_BUF
ld c,_RENAME
call DOS
ret c
ld c,_FNEXT
jr R_LRENLOOP
R_LRENEND: ld a,(LDIR_EXE)
or a
ret z
print OPCOMP_S
ret
;----------------
;--- LRMDIR ---
;----------------
R_LRMDIR: ld a,3
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,RESPONSE_BUF ;Extrae nombre de directorio
ld a,2
call EXTPAR
jp c,R_INVPAR
ld de,RESPONSE_BUF ;Si hay comodines, error "Invalid filename"
ld b,0
ld c,_PARSE
call DO_DOS
bit 5,b
ld a,#DA ;"Invalid filename"
jp nz,DOS2
ld c,_FFIRST ;Error si no encuentra el fichero/directorio
ld de,RESPONSE_BUF ;con ese nombre, o si lo encuentra pero
ld b,%10111 ;es un fichero
ld ix,USER_COM_BUF+1
call DOS
ret c
ld a,(USER_COM_BUF+15)
bit 4,a
ld a,#D6 ;"Directory not found"
jp z,DOS2
ld de,USER_COM_BUF+1 ;Intenta borrar el directorio
ld c,_DELETE
call DOS
ret c
print OPCOMP_S
ret
;------------
;--- LS ---
;------------
R_LS: ld hl,C_NLST
jp R_DIR_LS
;---------------
;--- LSHOW ---
;---------------
R_LSHOW: ld a,3
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF ;Extrae nombre de fichero
ld a,2
call EXTPAR
jp c,R_INVPAR
ld de,PARSE_BUF ;Si hay comodines, error "Invalid filename"
ld b,0
ld c,_PARSE
call DO_DOS
bit 5,b
ld a,#DA ;"Invalid filename"
jp nz,DOS2
ld a,(FILE_FH) ;Si ya hay un fichero abierto,
cp #FF ;lo cierra primero (no deberia pasar nunca)
jr z,R_LSHOW_OK1
ld c,_CLOSE
ld b,a
call DO_DOS
R_LSHOW_OK1: ;
ld a,1 ;Abre el fichero, termina si hay error
ld de,PARSE_BUF
ld c,_OPEN
call DOS
ret c
ld a,b
ld (FILE_FH),a
R_LSHOW_LOOP1: xor a
ld (LINE_COUNT),a
ld (BYTE_COUNT),a
R_LSHOW_LOOP: call GET_LDATA ;Obtiene un byte y lo imprime
jr c,GET_LSHOW_END
call PRINT_PAUSE
jr R_LSHOW_LOOP
GET_LSHOW_END: ld a,(FILE_FH) ;No quedan datos: cierra el fichero
ld b,a
ld c,_CLOSE
call DO_DOS
ld a,#FF
ld (FILE_FH),a
ret
;-----------------
;--- MDELETE ---
;-----------------
R_MDELETE: ld a,(HAY_NMAN)
or a
jr nz,R_MDEL2
print NONMAN_S
ret
R_MDEL2: ld hl,R_DELETE
ld (MUL_COM),hl
ld hl,DELETER_S
ld (MUL_STR),hl
jp MULTIPLE
;--------------
;--- MGET ---
;--------------
R_MGET: ld a,(HAY_NMAN)
or a
jr nz,R_MGET2
print NONMAN_S
ret
R_MGET2: ld hl,R_GET
ld (MUL_COM),hl
ld hl,RECEIVE_S
ld (MUL_STR),hl
jp MULTIPLE
;---------------
;--- MKDIR ---
;---------------
R_MKDIR: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld a,#FF
ld (ES_XCOM),a
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+4
ld a,2
call EXTPAR
ld hl,C_MKD
jp nc,PARAM_XCOM
jp R_INVPAR
;--------------
;--- MPUT ---
;--------------
R_MPUT: ld a,(HAY_NMAN)
or a
jr nz,R_MPUT2
print NONMAN_S
ret
R_MPUT2: ld a,4
ld (ABORT_STAT),a
ld a,#FF
ld (LDIR_EXE),a
ld a,(VERBOSE)
ld (OLD_VERBOSE_M),a
call _R_MPUT
xor a
ld (LDIR_EXE),a
ld a,(MUL_LISTA)
ld ix,0
nesman 22
ld a,#FF
ld (MUL_LISTA),a
ld a,(OLD_VERBOSE_M)
ld (VERBOSE),a
ret
_R_MPUT: call CHK_CON
ret c
ld hl,USER_COM_BUF+1
ld de,RESPONSE_BUF ;Extrae mascara
ld a,2
call EXTPAR
jp c,R_INVPAR
or a ;Crea lista interna
nesman 20
jr c,R_MPUT_OOME
ld (MUL_LISTA),a
;--- Primero busca todos los archivos y guarda
; sus nombres en la lista
ld c,_FFIRST
ld de,RESPONSE_BUF
ld b,0
R_MPUTLOOP1: ld ix,USER_COM_BUF+1
call DOS ;Realiza la busqueda
jp c,RMPUT_OKLIST
ld a,(MUL_LISTA)
ld ix,0
ld b,3
ld hl,13
ld iy,USER_COM_BUF+2
nesman 24
jr c,R_MPUT_OOME
ld c,_FNEXT
jr R_MPUTLOOP1
RMPUT_OKLIST: ;
;--- Ya tenemos los nombres, ahora saltamos a MULTIPLE
xor a
ld (MUST_ASCII),a
ld hl,R_SEND
ld (MUL_COM),hl
ld hl,SENDFILE_S
ld (MUL_STR),hl
jp MULTIPLE2
R_MPUT_OOME: print OUTOFM_S
ret
;--------------
;--- OPEN ---
;--------------
R_OPEN: ld a,#FF ;Primero actualiza informacion
ld (QUITTING),a ;sobre la conexion
call CHK_CON
ld a,0
ld (QUITTING),a
jr c,R_OPEN1
ld de,ALCON_S ;Ya estamos conectados?
ld c,_STROUT
jp nz,DO_DOS
R_OPEN1: ld a,4
ld (ABORT_STAT),a
;--- Extrae segundo parametro si lo hay (numero de puerto)
; y lo establece en el pseudo-TCB
ld hl,21 ;Puertos por defecto
ld (PORT_REMOTE_C),hl
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF
ld a,3
call EXTPAR
jr c,R_OPEN2
ld hl,PARSE_BUF
call EXTNUM
jp c,R_INVPAR
ld a,e
or a
jp nz,R_INVPAR
ld (PORT_REMOTE_C),bc
R_OPEN2: ;
;--- Extrae el primer parametro (nombre de host)
; y lo resuelve
ld hl,USER_COM_BUF+1 ;Extrae nombre
ld de,SERVER_NAME
ld a,2
call EXTPAR
jp c,R_INVPAR
print CONNECTING_S
ld b,0
ld a,TCPIP_DNS_Q
ld hl,SERVER_NAME ;Hace el query al DNS
;debug "1"
call CALL_UNAPI
;debug "2"
or a
jr z,R_OPEN_L1
ld de,WHENDNS_S ;Error devuelto por IP_DNS_Q?
ld hl,DNSQERRS_T
jp SHOW_ERR
R_OPEN_L1: ld a,TCPIP_WAIT ;Espera un resultado o un error del DNS
call CALL_UNAPI
ld b,0
ld a,TCPIP_DNS_S
call CALL_UNAPI
or a
jr nz,R_OPEN_L1!
ld a,b
cp 1
jr z,R_OPEN_L1
jr R_OPEN3
R_OPEN_L1!:
ld a,b ;Error devuelto por IP_DNS_S?
ld de,WHENDNS_S
ld hl,DNSERRS_T
jp SHOW_ERR
R_OPEN3: ld (IP_REMOTE_C),hl ;Establece las IPs en los TCBs
ld (IP_REMOTE_C+2),de
;--- Abre la conexion de control al servidor FTP
; y la conexion para el protocolo IDENT
ld hl,0
ld de,0
ld iy,113 ;Conx pasiva a nuestro puerto 113
ld bc,0
ld a,#FF
;debug "3"
call TCP_OPEN
;debug "4"
jr c,OK_IDENT
ld (IDENT_CON),a
OK_IDENT: ;
ld hl,(IP_REMOTE_C)
ld de,(IP_REMOTE_C+2)
ld ix,(PORT_REMOTE_C)
ld iy,#FFFF
ld bc,0
xor a
;debug "5"
call TCP_OPEN ;Intenta abrir conexion
;debug "6"
ld b,a
jr nc,R_OPEN_L2!
push af
ld a,(IDENT_CON)
;debug "7"
call TCP_ABORT
;debug "8"
pop af
ld de,WHENCONN_S
ld hl,OPENERR_T
jp SHOW_ERR
R_OPEN_L2!: ;ld (CONTROL_CON),a ;1.1
R_OPEN_L2: call IDENT_AUTOM
push bc ;Espera hasta que este ESTABLISHED
call HALT
ld a,b ;o hasta que se cierre ("Error: connection refused")
call TCP_STATUS
pop bc
or a
jr nz,OPENNOREF
ld a,(IDENT_CON)
call TCP_ABORT
ld a,#FF
ld (IDENT_CON),a
ld de,CONREF_S
ld c,_STROUT
or a
jp DO_DOS
OPENNOREF: ld d,a
ld a,(#FBEC) ;El bit 2 de #FBEC es 0
bit 2,a ;cuando se esta pulsando ESC
ld a,d
jr nz,NOOPCANCEL
ld a,b
call TCP_ABORT
ld a,#FF
ld (CONTROL_CON),a
print CANCELLED_S
ret
NOOPCANCEL: ;ld a,b
cp 4
jr nz,R_OPEN_L2
ld a,b
ld (CONTROL_CON),a
print OK_S
;--- Espera una respuesta que no sea 1yz
R_OPEN_L3: call IDENT_AUTOM
call WAIT_REPLY
ret c
cp 1
jr z,R_OPEN_L3
cp 4 ;Si la respuesta es 4yz, termina
ret z
cp 2 ;Si no es 2yz, "ERROR: Unexpected reply" y fin.
jr z,R_OPEN4
R_UNEX: print UNEXPEC_S
ret
R_OPEN4: ;
;>>> Ahora empieza la secuencia USER - PASSWORD - ACCOUNT.
call IDENT_AUTOM
ld a,(IDENT_CON)
call TCP_ABORT
;--- Pregunta el nombre de usuario, y lo envia mediante comando USER
_R_USER: print USER._S ;Obtiene nombre de usuario
printz DEF_USER
print CIERRAPAR_S
ld de,USER_COM_BUF
ld c,_BUFIN
call DO_DOS
call CRLF
_R_USER2: ld hl,C_USER ;Compone "USER usuario"+CRLF en SEND_COM_BUF
call SET_COMMAND
call SET_SPACE
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
ld a,1
call EXTPAR
jr nc,RUSER2
ld hl,DEF_USER ;DEF_USER si no se escribe nada
ld de,SEND_COM_BUF+5
ld bc,255
ldir
RUSER2: call APPEND_LF
;debug "1"
call SEND_COM ;Envia comando
;debug "2"
ret c
;debug "3"
call WAIT_REPLY ;Respuesta 2, 4 o 5: termina
;debug "4"
ret c
cp 4
ret z
cp 5
ret z
cp 2
ret z
cp 3 ;Respuesta 3: continua; otra: error "Unexpected reply"
jp nz,R_UNEX
;--- Se requiere contrasenya: se pregunta y se envia
RPASS2: print PASSWORD._S ;Obtiene password
;debug "5"
ld a,(DEF_PASSWORD)
or a
ld de,NONE_S
jr z,RPASS2_0
ld de,DEF_PASSWORD
call SET_HOOK
RPASS2_0: call PRINT_Z
call RESET_HOOK
print CIERRAPAR_S
ld de,USER_COM_BUF
call SET_HOOK
ld c,_BUFIN
call DO_DOS
call RESET_HOOK
ld hl,C_PASS ;Compone "PASS password"+CRLF en SEND_COM_BUF
call SET_COMMAND
call SET_SPACE
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
ld a,1
call EXTPAR
jr nc,RPASS2_1
ld a,(DEF_PASSWORD) ;Si no se ha introducido password:
or a ;Comprueba si hay password por
jr z,RPASS2 ;defecto y en ese caso lo usa,
ld hl,DEF_PASSWORD ;en caso contrario vuelve a preguntarlo
ld de,SEND_COM_BUF+5
ld bc,255
ldir
RPASS2_1: call APPEND_LF
call LF
call SEND_COM ;Envia comando
ret c
call WAIT_REPLY ;Respuesta 2, 4 o 5: termina
ret c
cp 4
ret z
cp 5
ret z
cp 2
ret z
cp 3 ;Respuesta 3: continua; otra: error "Unexpected reply"
jp nz,R_UNEX
;--- Se requiere "account": se pregunta y se envia
RACCT2: print ACCOUNT._S ;Obtiene account
ld a,(DEF_ACCOUNT)
or a
ld de,NONE_S
jr z,RACCT2_0
ld de,DEF_ACCOUNT
RACCT2_0: call PRINT_Z
print CIERRAPAR_S
ld de,USER_COM_BUF
ld c,_BUFIN
call DO_DOS
ld hl,C_ACCT ;Compone "ACCT account"+CRLF en SEND_COM_BUF
call SET_COMMAND
call SET_SPACE
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
ld a,1
call EXTPAR
jr nc,RACCT2_1
ld a,(DEF_ACCOUNT) ;Si no se ha introducido account:
or a ;Comprueba si hay account por
jr z,RACCT2 ;defecto y en ese caso lo usa,
ld hl,DEF_ACCOUNT ;en caso contrario vuelve a preguntarlo
ld de,SEND_COM_BUF+5
ld bc,255
ldir
RACCT2_1: call APPEND_LF
call LF
call SEND_COM ;Envia comando
ret c
call WAIT_REPLY ;Respuesta 2, 4 o 5: termina
ret c
cp 4
ret z
cp 5
ret z
cp 2
ret z
jp R_UNEX ;Otra: error "Unexpected reply"
;-----------------
;--- PASSIVE ---
;-----------------
R_PASSIVE: ld a,3
ld (ABORT_STAT),a
ld hl,PASSIVE
ld de,PASSIVE_M_S
jp TURN_ON_OFF
;---------------
;--- PAUSE ---
;---------------
R_PAUSE: ld a,3
ld (ABORT_STAT),a
ld hl,PAUSE
ld de,PAUSE_M_S
jp TURN_ON_OFF
;----------------
;--- PROMPT ---
;----------------
R_PROMPT: ld a,3
ld (ABORT_STAT),a
ld hl,PROMPT
ld de,PROMPT_M_S
jp TURN_ON_OFF
;-------------
;--- PWD ---
;-------------
R_PWD: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld a,#FF
ld (ES_XCOM),a
ld a,(VERBOSE)
push af ;Siempre muestra el resultado,
ld a,#FF ;aunque "verbose" sea 0
ld (VERBOSE),a
ld hl,C_PWD
call SINGLE_XCOM
pop af
ld (VERBOSE),a
ret
;--------------
;--- QUIT ---
;--------------
R_QUIT: ld a,#FF
ld (QUITTING),a
call CHK_CON
jp c,TERM
ld a,5
ld (ABORT_STAT),a
ld hl,C_QUIT
call SET_COMMAND
call APPEND_LF
call AUTOMATA_NO1
call HALT
call HALT
call HALT
jp TERM
;--------------------
;--- REMOTEHELP ---
;--------------------
R_REMOTEHELP: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld a,2
ld hl,USER_COM_BUF+1 ;No hay parametros: solo envia "HELP"
ld de,PARSE_BUF
call EXTPAR
ld hl,C_HELP
jp c,SINGLE_COM
ld hl,C_HELP ;Hay parametros: los envia todos
call SET_COMMAND ;tras el HELP, usando la rutina de "LITERAL"
call SET_SPACE
ld de,SEND_COM_BUF+5
ld a,(VERBOSE)
push af
ld a,#FF ;Siempre muestra respuesta
ld (VERBOSE),a
ld a,2
call LITELOOP
pop af
ld (VERBOSE),a
ret
;----------------
;--- RENAME ---
;----------------
R_RENAME: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld a,2
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
call EXTPAR
cp 3
jp nz,R_INVPAR ;Error si no hay dos parametros
ld hl,C_RNFR ;Envia "RNFR..."
call PARAM_COM
cp 3
jr z,R_REN2 ;Si respuesta 2yz, termina con
cp 1
ret z
cp 4
ret z
cp 5
ret z
print UNEXPEC_S ;error "Unexpected reply"
ret
R_REN2: ;
ld a,3 ;Envia "RNTO..."
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
call EXTPAR
ld hl,C_RNTO
call PARAM_COM
cp 1
ret z
cp 2
ret z
cp 4
ret z
cp 5
ret z
ld c,_STROUT ;Si la respuesta no es "2yz", "4yz" o "5yz",
ld de,UNEXPEC_S ;imprime "Unexpected reply"
call z,5
ret
;---------------
;--- RMDIR ---
;---------------
R_RMDIR: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld a,#FF
ld (ES_XCOM),a
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+4
ld a,2
call EXTPAR
ld hl,C_RMD
jp nc,PARAM_XCOM
jp R_INVPAR
;--------------
;--- SEND ---
;--------------
R_SEND: ld a,2
ld hl,C_STOR
R_SEND_APP: ld (PUTR_PAR),a ;Comprueba si existe el parametro
ld (PUTR_COM),hl ;2 (SEND) o 3 (APPEND)
push af
ld a,4
ld (ABORT_STAT),a
pop af
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF
call EXTPAR
jp c,R_INVPAR
ld hl,USER_COM_BUF+1 ;Extrae el parametro 2 (fichero local)
ld de,PARSE_BUF
ld a,2
call EXTPAR
ld c,_OPEN
ld de,PARSE_BUF
ld a,1
ld b,0
call DOS
ret c
ld a,b
ld (FILE_FH),a
ld a,#FF
ld (PUT_EXE),a
call PUTRIEVE
xor a
ld (PUT_EXE),a
ret
;--------------
;--- SHOW ---
;--------------
R_SHOW: ld hl,C_RETR
jp R_DIR_LS
;----------------
;--- STATUS ---
;----------------
R_STATUS: ld a,3
ld (ABORT_STAT),a
ld a,#FF ;Primero actualiza informacion
ld (QUITTING),a ;sobre la conexion
call CHK_CON
xor a
ld (QUITTING),a
ld a,(CONTROL_CON)
cp #FF
jr nz,STATR_2
call CRLF
print DISCON_S
jr STATR_3
STATR_2: print CONNTO_S
printz SERVER_NAME
call CRLF
STATR_3: ;
print TYPE_S
ld a,(TYPE)
or a
ld de,ASCII_S
jr z,STATR_4
ld de,BINARY_S
STATR_4: ld c,_STROUT
call DO_DOS
ld hl,VERBOSE
ld de,VERBOSE_S
call SHOW_ON_OFF
ld hl,DEBUG
ld de,DEBUG_S
call SHOW_ON_OFF
ld hl,PROMPT
ld de,PROMPT_S
call SHOW_ON_OFF
ld hl,HASH
ld de,HASH_S
call SHOW_ON_OFF
ld hl,BELL
ld de,BELL_S
call SHOW_ON_OFF
ld hl,PASSIVE
ld de,PASSIVE_S
call SHOW_ON_OFF
ld hl,PAUSE
ld de,PAUSE_S
call SHOW_ON_OFF
ld hl,XCOM
ld de,XCOM_S
call SHOW_ON_OFF
jp CRLF
;--------------
;--- TYPE ---
;--------------
R_TYPE: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF
ld a,2
call EXTPAR ;Error si no hay parametro
jp c,R_INVPAR
ld a,(PARSE_BUF+1) ;Error si el parametro es
or a ;de mas de una letra
jp nz,R_INVPAR
ld a,(PARSE_BUF) ;Error si no es "A" o "I",
or %00100000 ;si no hay error, salta a ASCII o BINARY
cp "a"
jp z,R_ASCII
cp "i"
jp z,R_BINARY
;--------------
;--- USER ---
;--------------
R_USER: call CHK_CON
ret c
ld a,4
ld (ABORT_STAT),a
ld hl,USER_COM_BUF+1
ld de,PARSE_BUF
ld a,2
call EXTPAR
jp c,_R_USER
ld a,b
ld (USER_COM_BUF+1),a
ld c,a
ld b,0
inc bc
ld hl,PARSE_BUF
ld de,USER_COM_BUF+2
ldir
jp _R_USER2
;-----------------
;--- VERBOSE ---
;-----------------
R_VERBOSE: ld a,3
ld (ABORT_STAT),a
ld hl,VERBOSE
ld de,VERBOSE_M_S
jp TURN_ON_OFF
;-------------------
;--- XCOMMANDS ---
;-------------------
R_XCOMMANDS: ld a,3
ld (ABORT_STAT),a
ld hl,XCOM
ld de,XCOM_M_S
jp TURN_ON_OFF
;--- Pruebas: comandos no implementados aun
NOIMP: macro @c
R_@c: ;jp R_NOIMP
endm
;NOIMP APPEND
R_NOIMP: print NOIMP_S
ret
;*** Rutinas auxiliares para la ejecucion de comandos
;--- SINGLE_COM: Ejecuta el comando HL sin parametros
SINGLE_COM: call SET_COMMAND
call APPEND_LF
jp AUTOMATA_NO1
;--- PARAM_COM: Ejecuta el comando HL con parametros ya establecidos
; (SEND_COM_BUF contiene "xxxxx")
PARAM_COM: call SET_COMMAND
call SET_SPACE
call APPEND_LF
jp AUTOMATA_NO1
;--- SINGLE_XCOM: Como SINGLE_COM, para comandos "X"
SINGLE_XCOM: call SET_COMMAND
call APPEND_LF
ld a,#FF
ld (ES_XCOM),a
jp AUTOMATA_NO1
;--- PARAM_XCOM: Como SINGLE_COM, para comandos "X"
PARAM_XCOM: call SET_COMMAND
call SET_SPACE
call APPEND_LF
ld a,#FF
ld (ES_XCOM),a
jp AUTOMATA_NO1
;--- R_INVPAR: Faltan parametros o son incorrectos
R_INVPAR: print INVPAR_S
ret
;******************************
;*** ***
;*** RUTINAS AUXILIARES ***
;*** ***
;******************************
;--------------------------------------------
;--- Rutinas de manipulacion de cadenas ---
;--- e impresion por pantalla ---
;--------------------------------------------
;--- NAME: COMP
; Compares HL and DE (16 bits in twos complement)
; INPUT: HL, DE = numbers to compare
; OUTPUT: C, NZ if HL > DE
; C, Z if HL = DE
; NC, NZ if HL < DE
COMP: call _COMP
ccf
ret
_COMP: ld a,h
sub d
ret nz
ld a,l
sub e
ret
;--- NAME: EXTPAR
; Extracts a parameter from the command line
; INPUT: A = Parameter to extract (the first one is 1)
; DE = Buffer to put the extracted parameter
; HL = Direccion de la cadena (el primer byte es la longitud)
; OUTPUT: A = Total number of parameters in the command line
; CY = 1 -> The specified parameter does not exist
; B undefined, buffer unmodified
; CY = 0 -> B = Parameter length, not including the tailing 0
; Parameter extracted to DE, finished with a 0 byte
; DE preserved
EXTPAR: or a ;Terminates with error if A = 0
scf
ret z
ld b,a
ld a,(hl) ;Terminates with error if
or a ;there are no parameters
scf
ret z
ld a,b
push hl,de,ix,iy
ld ix,0 ;IXl: Number of parameter
ld ixh,a ;IXh: Parameter to be extracted
inc hl
push hl
pop iy
;* Scans the command line and counts parameters
PASASPC: ld a,(hl) ;Skips spaces until a parameter
or a ;is found
jr z,ENDPNUM
cp 13
jr z,ENDPNUM
cp " "
inc hl
jri PASASPC
inc ix ;Increases number of parameters
PASAPAR: ld a,(hl) ;Walks through the parameter
or a
jr z,ENDPNUM
cp " "
inc hl
jri PASASPC
jr PASAPAR
;* Here we know already how many parameters are available
ENDPNUM: ld a,ixh ;Error if the parameter to extract
cp ixl ;is greater than the total number of
jrmy EXTPERR ;parameters available
push iy
pop hl
;ld hl,#81
ld b,1 ;B = current parameter
PASAP2: ld a,(hl) ;Skips spaces until the next
cp " " ;parameter is found
inc hl
jri PASAP2
ld a,ixh ;If it is the parameter we are
cp b ;searching for, we extract it,
jri PUTINDE0 ;else...
inc b
PASAP3: ld a,(hl) ;...we skip it and return to PASAP2
cp " "
inc hl
jrni PASAP3
jr PASAP2
;* Parameter is located, now copy it to the user buffer
PUTINDE0: ld b,0
dec hl
PUTINDE: inc b
ld a,(hl)
cp " "
jri ENDPUT
or a
jr z,ENDPUT
cp 13
jr z,ENDPUT
ld (de),a ;Paramete is copied to (DE)
inc de
inc hl
jr PUTINDE
ENDPUT: xor a
ld (de),a
dec b
ld a,ixl
or a
jr FINEXTP
EXTPERR: scf
FINEXTP: pop iy,ix,de,hl
ret
;--- Rutina de terminacion
; Aborta las conexiones abiertas, si las hay
; y borra la cola de nombres de fichero
TERM: xor a ;Desde ahora, CTRL-C no tiene efecto
ld (ABORT_STAT),a
;ld a,(DATA_CON)
;ld b,a
;ld a,(CONTROL_CON)
;and b
;cp #FF
;jr z,TERM_CON_OK
ld a,(DATA_CON)
cp #FF
call nz,TCP_CLOSE ;TCP_ABORT
ld a,(CONTROL_CON)
cp #FF
call nz,TCP_CLOSE ;TCP_ABORT
ld a,(IDENT_CON)
cp #FF
call nz,TCP_CLOSE
TERM_CON_OK: ;
TERM2:
ld a,(TPASLOT1)
ld h,#40
call ENASLT
ld a,(TPASEG1) ;Restaura TPA
call PUT_P1
ld a,(FILES_QUEUE) ;Borra cola de nombres de fichero
cp #FF
jr z,TERM_Q_OK
ld ix,0
nesman 22
TERM_Q_OK: ;
ld de,0 ;Desdefine rutina de CTRL-C
ld c,_DEFAB
call DO_DOS
ld bc,_TERM+0*256 ;Termina
jp DO_DOS
;--- Prints LF
CRLF: ld e,13
ld c,_CONOUT
call DO_DOS
LF: ld e,10
ld c,_CONOUT
jp DO_DOS
;--- Segment switching routines for page 1,
; these are overwritten with calls to
; mapper support routines on DOS 2
PUT_P1: out (#FD),a
ret
GET_P1: in a,(#FD)
ret
TPASEG1: db 2 ;TPA segment on page 1
;--- NAME: NUMTOASC
; Converts a 16 bit number into an ASCII string
; INPUT: DE = Number to convert
; HL = Buffer to put the generated ASCII string
; B = Total number of characters of the string
; not including any termination character
; C = Padding character
; The generated string is right justified,
; and the remaining space at the left is padded
; with the character indicated in C.
; If the generated string length is greater than
; the value specified in B, this value is ignored
; and the string length is the one needed for
; all the digits of the number.
; To compute length, termination character "$" or 00
; is not counted.
; A = &B ZPRFFTTT
; TTT = Format of the generated string number:
; 0: decimal
; 1: hexadecimal
; 2: hexadecimal, starting with "&H"
; 3: hexadecimal, starting with "#"
; 4: hexadecimal, finished with "H"
; 5: binary
; 6: binary, starting with "&B"
; 7: binary, finishing with "B"
; R = Range of the input number:
; 0: 0..65535 (unsigned integer)
; 1: -32768..32767 (twos complement integer)
; If the output format is binary,
; the number is assumed to be a 8 bit integer
; in the range 0.255 (unsigned).
; That is, bit R and register D are ignored.
; FF = How the string must finish:
; 0: No special finish
; 1: Add a "$" character at the end
; 2: Add a 00 character at the end
; 3: Set to 1 the bit 7 of the last character
; P = "+" sign:
; 0: Do not add a "+" sign to positive numbers
; 1: Add a "+" sign to positive numbers
; Z = Left zeros:
; 0: Remove left zeros
; 1: Do not remove left zeros
; OUTPUT: String generated in (HL)
; B = Length of the string, not including the padding
; C = Length of the string, including the padding
; Tailing "$" or 00 are not counted for the length
; All other registers are preserved
NUMTOASC: push af,ix,de,hl
ld ix,WorkNTOA
push af,af
and %00000111
ld (ix+0),a ;Type
pop af
and %00011000
rrca
rrca
rrca
ld (ix+1),a ;Finishing
pop af
and %11100000
rlca
rlca
rlca
ld (ix+6),a ;Flags: Z(zero), P(+ sign), R(range)
ld (ix+2),b ;Number of final characters
ld (ix+3),c ;Padding character
xor a
ld (ix+4),a ;Total length
ld (ix+5),a ;Number length
ld a,10
ld (ix+7),a ;Divisor = 10
ld (ix+13),l ;User buffer
ld (ix+14),h
ld hl,BufNTOA
ld (ix+10),l ;Internal buffer
ld (ix+11),h
ChkTipo: ld a,(ix+0) ;Set divisor to 2 or 16,
or a ;or leave it to 10
jr z,ChkBoH
cp 5
jp nc,EsBin
EsHexa: ld a,16
jr GTipo
EsBin: ld a,2
ld d,0
res 0,(ix+6) ;If binary, range is 0-255
GTipo: ld (ix+7),a
ChkBoH: ld a,(ix+0) ;Checks if a final "H" or "B"
cp 7 ;is desired
jp z,PonB
cp 4
jr nz,ChkTip2
PonH: ld a,"H"
jr PonHoB
PonB: ld a,"B"
PonHoB: ld (hl),a
inc hl
inc (ix+4)
inc (ix+5)
ChkTip2: ld a,d ;If the number is 0, never add sign
or e
jr z,NoSgn
bit 0,(ix+6) ;Checks range
jr z,SgnPos
ChkSgn: bit 7,d
jr z,SgnPos
SgnNeg: push hl ;Negates number
ld hl,0 ;Sign=0:no sign; 1:+; 2:-
xor a
sbc hl,de
ex de,hl
pop hl
ld a,2
jr FinSgn
SgnPos: bit 1,(ix+6)
jr z,NoSgn
ld a,1
jr FinSgn
NoSgn: xor a
FinSgn: ld (ix+12),a
ChkDoH: ld b,4
xor a
cp (ix+0)
jp z,EsDec
ld a,4
cp (ix+0)
jp nc,EsHexa2
EsBin2: ld b,8
jr EsHexa2
EsDec: ld b,5
EsHexa2: push de
Divide: push bc,hl ;DE/(IX+7)=DE, remaining A
ld a,d
ld c,e
ld d,0
ld e,(ix+7)
ld hl,0
ld b,16
BucDiv: rl c
rla
adc hl,hl
sbc hl,de
jr nc,$+3
add hl,de
ccf
djnz BucDiv
rl c
rla
ld d,a
ld e,c
ld a,l
pop hl,bc
ChkRest9: cp 10 ;Converts the remaining
jp nc,EsMay9 ;to a character
EsMen9: add a,"0"
jr PonEnBuf
EsMay9: sub 10
add a,"A"
PonEnBuf: ld (hl),a ;Puts character in the buffer
inc hl
inc (ix+4)
inc (ix+5)
djnz Divide
pop de
ChkECros: bit 2,(ix+6) ;Cchecks if zeros must be removed
jr nz,ChkAmp
dec hl
ld b,(ix+5)
dec b ;B=num. of digits to check
Chk1Cro: ld a,(hl)
cp "0"
jr nz,FinECeros
dec hl
dec (ix+4)
dec (ix+5)
djnz Chk1Cro
FinECeros: inc hl
ChkAmp: ld a,(ix+0) ;Puts "#", "&H" or "&B" if necessary
cp 2
jr z,PonAmpH
cp 3
jr z,PonAlm
cp 6
jr nz,PonSgn
PonAmpB: ld a,"B"
jr PonAmpHB
PonAlm: ld a,"#"
ld (hl),a
inc hl
inc (ix+4)
inc (ix+5)
jr PonSgn
PonAmpH: ld a,"H"
PonAmpHB: ld (hl),a
inc hl
ld a,"&"
ld (hl),a
inc hl
inc (ix+4)
inc (ix+4)
inc (ix+5)
inc (ix+5)
PonSgn: ld a,(ix+12) ;Puts sign
or a
jr z,ChkLon
SgnTipo: cp 1
jr nz,PonNeg
PonPos: ld a,"+"
jr PonPoN
jr ChkLon
PonNeg: ld a,"-"
PonPoN ld (hl),a
inc hl
inc (ix+4)
inc (ix+5)
ChkLon: ld a,(ix+2) ;Puts padding if necessary
cp (ix+4)
jp c,Invert
jr z,Invert
PonCars: sub (ix+4)
ld b,a
ld a,(ix+3)
Pon1Car: ld (hl),a
inc hl
inc (ix+4)
djnz Pon1Car
Invert: ld l,(ix+10)
ld h,(ix+11)
xor a ;Inverts the string
push hl
ld (ix+8),a
ld a,(ix+4)
dec a
ld e,a
ld d,0
add hl,de
ex de,hl
pop hl ;HL=initial buffer, DE=final buffer
ld a,(ix+4)
srl a
ld b,a
BucInv: push bc
ld a,(de)
ld b,(hl)
ex de,hl
ld (de),a
ld (hl),b
ex de,hl
inc hl
dec de
pop bc
ld a,b ;*** This part was missing on the
or a ;*** original routine
jr z,ToBufUs ;***
djnz BucInv
ToBufUs: ld l,(ix+10)
ld h,(ix+11)
ld e,(ix+13)
ld d,(ix+14)
ld c,(ix+4)
ld b,0
ldir
ex de,hl
ChkFin1: ld a,(ix+1) ;Checks if "$" or 00 finishing is desired
and %00000111
or a
jr z,Fin
cp 1
jr z,PonDolar
cp 2
jr z,PonChr0
PonBit7: dec hl
ld a,(hl)
or %10000000
ld (hl),a
jr Fin
PonChr0: xor a
jr PonDo0
PonDolar: ld a,"$"
PonDo0: ld (hl),a
inc (ix+4)
Fin: ld b,(ix+5)
ld c,(ix+4)
pop hl,de,ix,af
ret
WorkNTOA: defs 16
BufNTOA: ds 10
;--- NAME: EXTNUM
; Extracts a 5 digits number from an ASCII string
; INPUT: HL = ASCII string address
; OUTPUT: CY-BC = 17 bits extracted number
; D = number of digits of the number
; The number is considered to be completely extracted
; when a non-numeric character is found,
; or when already five characters have been processed.
; E = first non+numeric character found (or 6th digit)
; A = error:
; 0 => No error
; 1 => The number has more than five digits.
; CY-BC contains then the number composed with
; only the first five digits.
; All other registers are preserved.
EXTNUM: push hl,ix
ld ix,ACA
res 0,(ix)
set 1,(ix)
ld bc,0
ld de,0
BUSNUM: ld a,(hl) ;Jumps to FINEXT if no numeric character
ld e,a ;IXh = last read character
cp "0"
jr c,FINEXT
cp "9"+1
jr nc,FINEXT
ld a,d
cp 5
jr z,FINEXT
call POR10
SUMA: push hl ;BC = BC + A
push bc
pop hl
ld bc,0
ld a,e
sub "0"
ld c,a
add hl,bc
call c,BIT17
push hl
pop bc
pop hl
inc d
inc hl
jr BUSNUM
BIT17: set 0,(ix)
ret
ACA: db 0 ;b0: num>65535. b1: more than 5 digits
FINEXT: ld a,e
cp "0"
call c,NODESB
cp "9"+1
call nc,NODESB
ld a,(ix)
pop ix,hl
srl a
ret
NODESB: res 1,(ix)
ret
POR10: push de,hl ;BC = BC * 10
push bc
push bc
pop hl
pop de
ld b,3
ROTA: sla l
rl h
djnz ROTA
call c,BIT17
add hl,de
call c,BIT17
add hl,de
call c,BIT17
push hl
pop bc
pop hl,de
ret
;--- PRINT_L: Imprime la cadena DE, acabada en CRLF
PRINT_L: ld a,(de)
push af,de
ld e,a
ld c,_CONOUT
call DO_DOS
pop de,af
cp 10
ret z
inc de
jr PRINT_L
;--- PRINT_Z: Imprime la cadena DE, acabada en cero
PRINT_Z: ld a,(de)
or a
ret z
push de
ld e,a
ld c,_CONOUT
call DO_DOS
pop de
inc de
jr PRINT_Z
;--- SHOW_ERR: Muestra la cadena DE, y a continuacion
; busca un error con codigo A en la tabla HL, y lo muestra.
SHOW_ERR push af,hl,de
print ERROR_S
pop de
ld c,_STROUT
call DO_DOS
pop de,af
ld b,a
SEARCH_ERROR: ld a,(de)
inc de
cp b
jr z,ERROR_FOUND
NEXT_ERROR: ld a,(de)
inc de
or a
jr z,ERROR_NOTF
cp "$"
jr nz,NEXT_ERROR
jr SEARCH_ERROR
ERROR_NOTF: ld de,UNKERR_S
ERROR_FOUND: ld c,_STROUT ;Print error string
call DO_DOS
call LF
scf
ret
;--- SEARCH_COM: Busca un comando en la tabla de idems
; Entrada: HL = Dir. del comando, acabado en 0
; Salida: HL = Dir de la rutina del comando
; DE = Dir de la ayuda del comando
; Cy = 1 si no existe
SEARCH_COM: ld (COMM_DIR),hl
ld hl,COMMAND_TABLE
SRCH_C_START: ld de,(COMM_DIR)
ld a,(hl)
or a
scf
ret z
dec hl
dec de
SRCH_C_LOOP: inc hl
inc de
ld a,(de)
cp "A"
jrmn SRCH_C_LP2
and %11011111 ;Lo transforma a mayusculas
SRCH_C_LP2: cp (hl)
jr nz,NEXT_COM
or a
jr nz,SRCH_C_LOOP
inc hl
push hl ;Comando encontrado
pop ix
ld l,(ix)
ld h,(ix+1)
ld e,(ix+2)
ld d,(ix+3)
or a
ret
NEXT_COM: ld a,(hl)
inc hl
or a
jr nz,NEXT_COM
inc hl
inc hl
inc hl
inc hl
jr SRCH_C_START
COMM_DIR: dw 0
;--- TURN_ON_OFF: Intercambia el valor de una variable
; Entrada: HL = Variable
; DE = Cadena para imprimir "xxx turned ON/OFF"
TURN_ON_OFF: push de
ld a,(hl)
cpl
ld (hl),a
or a
ld hl,OFF_S
jr z,TONOFF2
ld hl,ON_S
TONOFF2: pop de
push hl
ld c,_STROUT
call DO_DOS
print TURNED_S
pop de
ld c,_STROUT
call DO_DOS
jp CRLF
;--- SHOW_ON_OFF: Muestra la cadena DE, y luego "ON" o "OFF"
; segun el valor de la variable HL.
SHOW_ON_OFF: push hl
ld c,_STROUT
call DO_DOS
pop hl
ld a,(hl)
or a
ld de,OFF_S
jr z,SHOWONOFF2
ld de,ON_S
SHOWONOFF2: ld c,_STROUT
call DO_DOS
jp CRLF
;--- APPEND_LF: Aqade CRLF al final de la cadena en SEND_COM_BUF (acabada en 0)
APPEND_LF: ld hl,SEND_COM_BUF-1
APLF_L: inc hl
ld a,(hl)
or a
jr nz,APLF_L
ld (hl),13
inc hl
ld (hl),10
ret
;--- SET_COMMAND: Copia el comando HL a SEND_COM_BUF, acabado en 0
SET_COMMAND: ld de,SEND_COM_BUF
ld bc,5
push hl
inc hl
inc hl
inc hl
ld a,(hl)
pop hl
or a
jr nz,SETCOM2
dec bc
SETCOM2: ldir
ret
;--- SET_SPACE: Pone un espacio tras el comando en SEND_COM_BUF
; (donde esta el 0)
SET_SPACE: ld hl,SEND_COM_BUF-1
SETSPACE2: inc hl
ld a,(hl)
or a
jr nz,SETSPACE2
ld (hl)," "
ret
;--- DOS: Llama a una funcion del DOS, y en caso de error,
; imprime su codigo y vuelve con Cy=1
DOS: call DO_DOS
or a
ret z
DOS2: push hl,de,bc
ld b,a ;Si se esta haciendo un DIR, el error #D7
ld a,(LDIR_EXE) ;(file not found) no se imprime
or a
jr z,DOS3
ld a,b
cp #D7
jr z,DOS5
DOS3: ld a,(PUT_EXE) ;Si se esta haciendo un PUT,
or a ;el error #C7 (end of file)
jr z,DOS4 ;no se imprime
ld a,b
cp #C7
jr z,DOS5
DOS4: ld de,RESPONSE_BUF
ld c,_EXPLAIN
call DO_DOS
print DISKERR_S
printz RESPONSE_BUF
call CRLF
DOS5: scf
pop bc,de,hl
ret
;--- PRINT_PATH: Muestra la unidad+directorio actual y un fin de linea
PRINT_PATH: ld c,_CURDRV ;Muestra unidad
call DO_DOS
add "A"
ld e,a
ld c,_CONOUT
call DO_DOS
ld e,":"
ld c,_CONOUT
call DO_DOS
ld e,"\"
ld c,_CONOUT
call DO_DOS
ld b,0 ;Muestra dir y CRLF
ld de,RESPONSE_BUF
ld c,_GETCD
call DO_DOS
printz RESPONSE_BUF
jp CRLF
;--- ASK_YNAC
; - Imprime "(y,n,a,c)?"
; - Obtiene un caracter del teclado
; - Devuelve 0,1,2,3 para y,n,a,c respectivamente.
ASK_YNAC: print YNAC_S
ASK_YNAC2: ld c,_INNOE
call DO_DOS
ld c,a
or %00100000
ld b,0
cp "y"
jr z,ASK_YNAC3
inc b
cp "n"
jr z,ASK_YNAC3
inc b
cp "a"
jr z,ASK_YNAC3
inc b
cp "c"
jr nz,ASK_YNAC2
ASK_YNAC3: push bc
ld e,c
ld c,_CONOUT
call DO_DOS
call CRLF
pop af
ret
;--- PRINT_PAUSE: Imprime el caracter A, y si PAUSE=#FF,
; realiza una pausa "Press any key to continue"
; cuando se muestra una pantalla llena.
; Se ha de inicializar con BYTE_COUNT y LINE_COUNT a 0.
PRINT_PAUSE: cp #C ;Si es "borrar panrtalla",
jr z,PPAUSE_CLS ;actua como pantalla llena
push af
ld e,a
ld c,_CONOUT
call DO_DOS
pop bc
ld a,(PAUSE)
or a
ret z
ld a,(BYTE_COUNT)
inc a ;Si se encuentra LF, o se han cogido 80 caracteres,
ld (BYTE_COUNT),a ;se cambia de linea
cp 80
jr z,PPAUSE_NEWL
ld a,b
cp 10
ret nz
PPAUSE_NEWL: xor a
ld (BYTE_COUNT),a
ld a,(LINE_COUNT) ;Si era LF, PAUSE=#FF, y era la linea 23,
inc a ;imprime "Press any key" y hace una pausa
ld (LINE_COUNT),a
cp 23
ret nz
PPAUSE_NEWS: xor a ;Pantalla llena
ld (LINE_COUNT),a
ld (BYTE_COUNT),a
print PRESS_S
ld c,_INNOE
call DO_DOS
push af
call CRLF
pop af
or %00100000
cp "p"
ret nz
xor a
ld (PAUSE),a
ret
PPAUSE_CLS: call PPAUSE_NEWS
ld e,#C
ld c,_CONOUT
jp DO_DOS
;--- CLOSE_FILE: Cierra fichero abierto si lo hay
CLOSE_FILE: ld a,(FILE_FH)
cp #FF
ret z
ld b,a
ld c,_CLOSE
call DO_DOS
ld a,#FF
ld (FILE_FH),a
ret
;-------------------------
;--- Rutinas TCP/FTP ---
;-------------------------
;--- CHK_CON: Comprueba la conexion con el servidor FTP.
; Si hay conexion, devuelve Cy=0.
; Si no hay conexion y CONTROL_CON=#FF, imprime "Disconnected" y devuelve Cy=1.
; Si no hay conexion y CONTROL_CON<>#FF, imprimer "ERROR" y devuelve Cy=1.
; Si QUITTING=#FF, no imprime nada.
CHK_CON: call _CHK_CON
ret nc
ld a,0
ld (TYPE),a
ret
_CHK_CON: ld a,(CONTROL_CON)
cp #FF
jr nz,CHK_CON2
;CONTROL_CON es #FF:
;Imprime "Disconnected" y devuelve Cy=1
ld a,(QUITTING)
or a
ld de,DISCON_S
ld c,_STROUT
call z,DO_DOS
scf
ret
;CONTROL_CON no es #FF:
;En teoria hay alguna conexion abierta
CHK_CON2: call TCP_STATUS
or a
jr z,CHK_CON3
cp 4
ret z ;Si estamos cerrando, no devolvemos error
ld a,(CONTROL_CON)
call TCP_CLOSE ;1.1
ld a,(QUITTING) ;aunque no este ESTABLISHED,
or a ;ya que pueden quedar datos por recoger
ld a,#FF
ld (CONTROL_CON),a
scf
ret nz
CHK_CON21: ;call GET_CDATA ;Si quedan datos pendientes,
;jr c,CHK_CON22 ;los imprime
;ld e,a ;(eliminado porque ahora siempre
;ld c,_CONOUT ;llamamos a R_FLUSH antes de
;call DO_DOS ;ejecutar cualquier comando)
;jr CHK_CON21
CHK_CON22: ;call CRLF
ld a,(CONTROL_CON)
call TCP_CLOSE ;TCP_ABORT ;No hay conexion abierta: imprime "ERROR"
CHK_CON3: ld a,#FF
ld (CONTROL_CON),a
xor a
ld (GCDATA_COUNT),a
ld a,(QUITTING)
or a
ld de,CONLOST_S
ld c,_STROUT
call z,DO_DOS
scf
ret
;--- SEND_COM: Envia el comando que hay en SEND_COM_BUF (acabado en CRLF).
; Si ES_XCOM=#FF y XCOMMANDS=#FF, envia la version "X"
; Si DEBUG=#FF, tambien lo imprime por pantalla.
; Devuelve Cy=1 si no hay conexion.
SEND_COM: ld hl,SEND_COM_BUF
ld bc,0 ;Mide la longitud del comando
SEND_COM2: inc bc
ld a,(hl)
inc hl
cp 10
jr nz,SEND_COM2
push bc
call CHK_CON
pop bc
ret c
ld hl,SEND_COM_BUF
ld de,(XCOM)
inc de
ld a,d
or e
jr nz,SEND_COM3 ;Decrementa el puntero al comando,
ld a,"X" ;incrementa la longitud y pone "X" al principio
ld (SEND_COM_BUF-1),a ;si XCOM y ES_XCOM son #FF
xor a
ld (ES_XCOM),a
dec hl
inc bc
SEND_COM3: push hl
ld a,(CONTROL_CON)
scf
call TCP_SEND
pop hl
jr c,SEND_COM_E
ld a,(DEBUG)
or a
ret z
push hl
print FLECHA_S
pop de
call PRINT_L
or a
ret
SEND_COM_E: print NOMEM_S
scf
ret
;--- WAIT_REPLY: Espera una respuesta del servidor y la encola en RESPONSE_BUF
; (una linea cada vez).
; Devuelve Cy=1 si no hay conexion.
; Devuelve Cy=0 y A=primer digito de la respuesta en caso contrario.
; Imprime la respuesta si VERBOSE=#FF (cada vez que hay una linea disponible).
WAIT_REPLY: ld a,#FF
ld (LAST_REPLY),a
call _WAIT_REPLY
ret c ;Si tras llamar a la rutina quedan datos,
ld (LAST_REPLY),a
call HALT
ld e,a ;se vuelve a llamar
push de
ld a,(CONTROL_CON)
call TCP_STATUS
pop de
ld a,h
or l
jr nz,WAIT_REPLY
ld a,e
or a
ret
_WAIT_REPLY: ;
xor a
ld (WREPLY_LINE),a
;--- Coge una linea completa
WREPLY_LOOP1: ld hl,RESPONSE_BUF
WREPLY_LOOP2: call HALT
ld a,(IDENT_CON)
cp #FF
call nz,IDENT_AUTOM ;!!! Mejorar esto
push hl
call CHK_CON
pop hl
ret c
WREPLY_LOOP3: push hl
call GET_CDATA
pop hl
jr c,WREPLY_LOOP2
ld (hl),a
inc hl
cp 10
jr nz,WREPLY_LOOP3
;--- Se ha cogido una linea completa:
; Actuamos segun si es la primera linea o no
ld a,(WREPLY_LINE)
or a
jr nz,WREPLY_NO1
;--- Primera linea
;Comprueba que el primer caracter sea un numero,
;de lo contrario ignora la linea
ld a,(RESPONSE_BUF)
cp "0"
jr c,WREPLY_LOOP1
cp "9"+1
jr nc,WREPLY_LOOP1
;Copia el codigo a WREPLY_CODE
ld hl,RESPONSE_BUF
ld de,WREPLY_CODE
ld bc,4
ldir
;Lo imprime si es un error, o si VERBOSE=#FF
call WREPLY_PRINT
;Si no hay mas lineas, termina
WREPLY_OK1: ld a,(RESPONSE_BUF+3)
cp " "
jr nz,WREPLY_HAYMAS
ld a,(RESPONSE_BUF)
sub "0"
or a
ret
WREPLY_HAYMAS: ld a,#FF
ld (WREPLY_LINE),a
jr WREPLY_LOOP1
;--- Siguientes lineas
WREPLY_NO1: ;
;Lo imprime si es un error, o si VERBOSE=#FF
call WREPLY_PRINT
;Comprueba si el codigo coincide con el que hay en WREPLY_CODE,
;en ese caso termina, si no, hay mas lineas
ld hl,RESPONSE_BUF
ld de,WREPLY_CODE
ld b,3
WREPLY_LOOP4: ld a,(de)
cp (hl)
inc hl
inc de
jp nz,WREPLY_LOOP1
djnz WREPLY_LOOP4
ld a,(hl)
cp " "
jp nz,WREPLY_LOOP1
ld a,(RESPONSE_BUF)
sub "0"
or a
ret
;--- Esta subrutina imprime la linea de RESPONSE_BUF
; si es un error o si VERBOSE=#FF
WREPLY_PRINT: ld a,(VERBOSE)
or a
jr nz,WREPLY_PRINT2
ld a,(RESPONSE_BUF)
cp "4"
jr z,WREPLY_PRE
cp "5"
ret nz
WREPLY_PRE: printl RESPONSE_BUF+4
jr WREPLY_OK1
WREPLY_PRINT2: printl RESPONSE_BUF
ret
WREPLY_LINE: db 0 ;Linea: 0=1a, #FF=otras
WREPLY_CODE: db "000 " ;Codigo de la 1a linea
;--- GET_CDATA: Obtiene un byte de datos en A de la conexion de control,
; usando un bufer de 256 bytes
GET_CDATA: ld a,(GCDATA_COUNT)
or a
jr z,GCDATA_NEW
dec a
ld (GCDATA_COUNT),a
ld hl,(GCDATA_PNT)
ld a,(hl)
inc hl
ld (GCDATA_PNT),hl
or a
ret
GCDATA_NEW: ld de,GDATA_CBUF
ld a,(CONTROL_CON)
ld bc,256
call TCP_RCV
ret c
scf
ret z
dec bc
ld a,c
ld (GCDATA_COUNT),a
ld hl,GDATA_CBUF+1
ld (GCDATA_PNT),hl
ld a,(GDATA_CBUF)
or a
ret
GCDATA_COUNT: db 0
GCDATA_PNT: dw 0
;--- GET_DDATA: Obtiene un byte de datos en A de la conexion de datos,
; usando un bufer de 256 bytes
GET_DDATA: ld a,(GDDATA_COUNT)
or a
jr z,GDDATA_NEW
dec a
ld (GDDATA_COUNT),a
ld hl,(GDDATA_PNT)
ld a,(hl)
inc hl
ld (GDDATA_PNT),hl
or a
ret
GDDATA_NEW: ld de,GDATA_DBUF
ld a,(DATA_CON)
ld bc,256
call TCP_RCV
ret c
scf
ret z
dec bc
ld a,c
ld (GDDATA_COUNT),a
ld hl,GDATA_DBUF+1
ld (GDDATA_PNT),hl
ld a,(GDATA_DBUF)
or a
ret
GDDATA_COUNT: db 0
GDDATA_PNT: dw 0
;--- GET_LDATA: Obtiene un byte de datos en A del fichero abierto en FILE_FH,
; usando un bufer de 256 bytes (el mismo que GET_DDATA)
GET_LDATA: ld a,(GLDATA_COUNT)
or a
jr z,GLDATA_NEW
dec a
ld (GLDATA_COUNT),a
ld hl,(GLDATA_PNT)
ld a,(hl)
inc hl
ld (GLDATA_PNT),hl
or a
ret
GLDATA_NEW: ld a,(FILE_FH)
cp #FF
scf
ret z
ld b,a
ld de,GDATA_DBUF
ld hl,256
ld c,_READ
call DO_DOS
or a
scf
ret nz
dec hl
ld a,l
ld (GLDATA_COUNT),a
ld hl,GDATA_DBUF+1
ld (GLDATA_PNT),hl
or a
ld a,(GDATA_DBUF)
or a
ret
GLDATA_COUNT: db 0
GLDATA_PNT: dw 0
;--- AUTOMATA_NO1: Automata FTP para comandos simples,
; que no pueden devolver un codigo 1yz.
; Envia el comando que hay en SEND_COM_BUF y espera una respuesta.
; Si la respuesta no es 2yz, 4yz o 5yz, imprime "ERROR: Unexpected reply".
AUTOMATA_NO1: call SEND_COM
ret c
call WAIT_REPLY
ret c
cp 2
ret z
cp 4
ret z
cp 5
ret z
print UNEXPEC_S
scf
ret
;--- HALT: Espera a la siguiente interrupcion
HALT: push af,bc,de,hl
ld a,TCPIP_WAIT
call CALL_UNAPI
;ld c,#B
;call DO_DOS
pop hl,de,bc,af
ret
;--- OPEN_DCON: Abre una conexion de datos activa o pasiva
OPEN_DCON: ld a,(PASSIVE)
or a
jp z,OPEN_DCON_A
;--- OPEN_DCON_P:
; - Envia un comando PASV
; - Espera la respuesta e interpreta la IP y puerto devueltos
; - Abre una conexion activa de datos con esa informacion
; - Si no puede abrir la conexion, o hay error al enviar el PORT,
; imprime el error y vuelve con Cy=1
OPEN_DCON_P: ld a,(DATA_CON)
cp #FF
jr z,OPEN_DCONP2
call TCP_ABORT
OPEN_DCONP2: call HALT
ld hl,C_PASV
ld de,SEND_COM_BUF
ld bc,5
ldir
call APPEND_LF
call AUTOMATA_NO1 ;Envia comando PASV y espera respuesta,
cp 2 ;termina si no es 2yz
jp nz,OPEN_DCON_E
;* Busca en la respuesta el primer caracter numerico
ld hl,RESPONSE_BUF+3
OPEN_DCP_L1: inc hl
ld a,(hl)
cp "0"
jr c,OPEN_DCP_L1
cp "9"+1
jr nc,OPEN_DCP_L1
;* Ahora extrae la IP y el puerto, los va guardando en DATA_BUF
ld de,DATA_BUF
ld b,6 ;4 bytes para IP y 2 para puerto
OPEN_DCP_L2: push bc,de
call EXTNUM
ld e,d ;Hace que HL apunte al siguiente numero
ld d,0
add hl,de
inc hl
pop de ;Almacena el numero y pasa al siguiente
ld a,c
ld (de),a
inc de
pop bc
djnz OPEN_DCP_L2
;* Abre la conexion con los datos obtenidos
ld hl,(DATA_BUF)
ld de,(DATA_BUF+2)
ld a,(DATA_BUF+4)
ld ixh,a
ld a,(DATA_BUF+5)
ld ixl,a
ld iy,#FFFF
xor a
ld bc,0
call TCP_OPEN
ld de,WHENOPEND_S
ld hl,OPENERR_T
jp c,SHOW_ERR
ld (DATA_CON),a
or a
ret
;--- OPEN_DCON_A:
; - Abre una conexion pasiva de datos
; - Envia un comando PORT y espera respuesta
; - Si no puede abrir la conexion, o hay error al enviar el PORT,
; imprime el error y vuelve con Cy=1
OPEN_DCON_A: ld a,(DATA_CON)
cp #FF
jr z,OPEN_DCON2
call TCP_ABORT
OPEN_DCON2: call HALT
ld hl,0
ld de,0
ld iy,#FFFF
ld bc,0
ld a,#FF
call TCP_OPEN ;Abre conexion pasiva
ld de,WHENOPEND_S
ld hl,OPENERR_T
jp c,SHOW_ERR
ld (DATA_CON),a
ld b,a
ld hl,TCP_INFO_BLOCK
ld a,TCPIP_TCP_STATE ;Establece lo que queda de la cadena
call CALL_UNAPI
or a
jr nz,OPEN_DCON_E
ld de,(TCP_INFO_BLOCK+6) ;Lee el puerto local
ld bc,(PORT_C_PNT)
push bc
ld a,d
push de
call SET_IP
pop de
inc hl
ld a,e
call SET_IP
ld hl,(PORT_C_PNT)
dec hl
ld (hl),0
pop hl
ld (PORT_C_PNT),hl
ld hl,C_PORT
ld de,SEND_COM_BUF
ld bc,32
ldir
call APPEND_LF
call AUTOMATA_NO1 ;Envia comando PORT y espera respuesta,
cp 2 ;termina si no es 2yz
jr nz,OPEN_DCON_E
or a
ret
OPEN_DCON_E: ld a,(DATA_CON)
call TCP_ABORT
ld a,#FF
ld (DATA_CON),a
scf
ret
;--- PUTRIEVE: Rutina generica para enviar datos con el comando PUT:
; - Envia el comando PORT y abre una conexion de datos
; - Envia los datos del fichero FILE_FH
; - El comando a enviar sera (PUTR_COM),
; junto a la palabra que haya
; en USER_COM_BUF en el lugar PUTR_PAR
; Vuelve con Cy=1 si hay cualquier error
PUTRIEVE: call CHK_CON
jp c,PUTR_END
;--- Abre conexion,
; envia PORT, espera conexion OK
call OPEN_DCON ;Abre conexion, envia PORT
jp c,PUTR_END
ld hl,(PUTR_COM) ;Envia comando con el parametro
call SET_COMMAND
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
ld a,(PUTR_PAR)
call EXTPAR
call nc,SET_SPACE
call APPEND_LF
call SEND_COM
call WAIT_REPLY ;Espera respuesta, que ha de
jp c,PUTR_E ;ser 1yz
cp 1
jr z,PUTR_WAIT
cp 4
jp z,PUTR_E
cp 5
jp z,PUTR_E
jp PUTR_EX
PUTR_WAIT: call HALT ;Espera a que la conexion este
ld a,(DATA_CON) ;o ESTABLISHED o cerrandose
call TCP_STATUS ;(ya se han recibido todos los datos)
or a
jp z,PUTR_E2
cp 4
jr c,PUTR_WAIT
;--- Conexion abierta: va leyendo datos del fichero
; y los envia a la conexion
PUTR_LOOP: ld a,(FILE_FH)
ld b,a
ld c,_READ
ld hl,512
ld de,DATA_BUF
call DOS
jr nc,PUTR_RDOK
ld a,(HASH)
or a
call nz,CRLF
ld a,(DATA_CON)
call TCP_CLOSE
call HALT
call WAIT_REPLY ;Fin del fichero: cierra conexcion de datos,
jp c,PUTR_END ;espera respuesta de la conexion de control,
cp 2 ;y termina
jp z,PUTR_END
cp 4
jp z,PUTR_END
cp 5
jp z,PUTR_END
print UNEXPEC_S
scf
jp PUTR_END
PUTR_RDOK: push hl
pop bc
PUTR_LOP2: push bc
call HALT
ld a,(DATA_CON)
ld hl,DATA_BUF
scf
call TCP_SEND ;Intenta enviar datos
pop bc
jr nc,PUTR_OKDATA
cp ERR_BUFFER
jr z,PUTR_LOP2
ld a,(DATA_CON) ;Conexion cerrada antes de tiempo?
call TCP_ABORT ;Entonces mostramos error
ld a,(HASH)
or a
call nz,CRLF
ld a,#FF
ld (DATA_CON),a
print DCONREF_S
;call WAIT_REPLY ;Conexion cerrada legalmente: la cerramos nosotros,
;jp c,PUTR_END ;espera respuesta de la conexion de control,
;cp 2 ;y termina
;jp z,PUTR_END
;cp 4
;jp z,PUTR_END
;cp 5
;jp z,PUTR_END
;print UNEXPEC_S
scf
jr PUTR_END
PUTR_OKDATA: ;
ld a,(HASH)
or a
ld e,"#"
ld c,_CONOUT
call nz,DO_DOS
jp PUTR_LOOP
;--- Rutinas de terminacion
PUTR_E: ld a,(DATA_CON) ;Error del comando "PORT":
call TCP_ABORT ;cierra conexion y termina
scf
jp PUTR_END
PUTR_E2: print DCONREF_S ;Conexion de datos cerrada sin llegar
ld a,(DATA_CON)
call TCP_ABORT
call WAIT_REPLY ;a abrirse (probable perdida de con. a internet)
scf
jp PUTR_END
PUTR_EX: ld a,(DATA_CON) ;Respuesta inesperada al
call TCP_ABORT ;comando "PORT"
call R_UNEX
scf
;jp PUTR_END
PUTR_END: push af
ld a,(FILE_FH)
cp #FF
jr z,PUTR_END2
ld a,(FILE_FH)
ld b,a
ld c,_CLOSE
call DO_DOS
ld a,#FF
ld (FILE_FH),a
PUTR_END2: pop af
ret
PUT_EXE: db 0
PUTR_COM: dw 0
;--- RETRIEVE: Rutina generica para obtener datos
; desde una conexion de datos:
; - Usa SILENT_ASCII y SILENT_BIN si MUST_ASCII=#FF
; - Envia el comando PORT y abre una conexion de datos
; - Lee los datos recibidos. Si FILE_FH=#FF, los
; imprime por pantalla. Si no, los escribe en el fichero.
; - El comando a enviar ha de estar en RETR_COM;
; se envia junto a la palabra que haya
; en USER_COM_BUF en el lugar RETR_PAR
; Vuelve con Cy=1 si hay cualquier error
RETRIEVE: call CHK_CON
jp c,RETR_END
;--- Abre conexion, envia TYPE A si es necesario,
; envia PORT, espera conexion OK
ld a,(MUST_ASCII)
or a
jr z,RETRV2
call SILENT_ASCII ;Pone modo ASCII si es necesario
jp c,RETR_END
ld a,(LAST_REPLY) ;Si "TYPE A" genera error,
cp 2 ;no seguimos
jr z,RETRV2
xor a
ld (MUST_ASCII),a
jp RETR_END
RETRV2: call OPEN_DCON ;Abre conexion, envia PORT
jr nc,RETRV3
;xor a
;ld (MUST_ASCII),a
jp RETR_END
RETRV3: ld hl,(RETR_COM) ;Envia comando con el parametro
call SET_COMMAND
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
ld a,(RETR_PAR)
call EXTPAR
call nc,SET_SPACE
call APPEND_LF
call SEND_COM
call _WAIT_REPLY ;Espera respuesta, que ha de
jp c,RETR_E ;ser 1yz
cp 1
jr z,RETR_WAIT
cp 4
jp z,RETR_E
cp 5
jp z,RETR_E
jp RETR_EX
RETR_WAIT: call HALT ;Espera a que la conexion este
ld a,(DATA_CON) ;o ESTABLISHED o cerrandose
call TCP_STATUS ;(ya se han recibido todos los datos)
or a
jp z,RETR_E2
cp 4
jr c,RETR_WAIT
xor a
ld (LINE_COUNT),a
ld (BYTE_COUNT),a
;--- Conexion abierta: va leyendo datos
; y los imprime o los guarda en el fichero
RETR_LOOP: call HALT
ld a,(DATA_CON)
ld de,DATA_BUF
ld bc,512
call TCP_RCV ;Intenta recibir datos
jr nz,RETR_OKDATA
;cp 5 ;Si no hay datos pero la conexion sigue ESTABLISHED
;jp nz,RETR_E2 ;(puede recibir mas datos), sigue esperando
ld a,(DATA_CON)
call TCP_STATUS
cp 4
jr z,RETR_LOOP
ld a,(DATA_CON)
call TCP_CLOSE ;TCP_ABORT
ld a,(HASH)
or a
call nz,CRLF
ld a,#FF
ld (DATA_CON),a
call WAIT_REPLY ;Conexion cerrada legalmente: la cerramos nosotros,
jp c,RETR_END ;espera respuesta de la conexion de control,
cp 2 ;y termina
jp z,RETR_END
cp 4
jp z,RETR_END
cp 5
jp z,RETR_END
print UNEXPEC_S
scf
jp RETR_END
RETR_OKDATA: ;
ld a,(FILE_FH)
cp #FF
jr nz,RETR_FILE
;--- FILE_FH=#FF: Imprime los datos en pantalla
ld hl,DATA_BUF ;Imprime todos los datos recibidos
RETR_LOOP2: push bc,hl
ld a,(hl)
call PRINT_PAUSE
pop hl
inc hl
pop bc
dec bc
ld a,b
or c
jr nz,RETR_LOOP2
jp RETR_LOOP
;--- FILE_FH<>#FF: Guarda los datos en el fichero
RETR_FILE: ld a,(HASH)
or a
ld e,"#"
push bc
ld c,_CONOUT
call nz,DO_DOS
pop hl
ld a,(FILE_FH)
ld b,a
ld c,_WRITE
ld de,DATA_BUF
call DOS
jr c,RETR_E
jp RETR_LOOP
;--- Rutinas de terminacion
RETR_E: ld a,(DATA_CON) ;Error del comando "PORT":
call TCP_ABORT ;cierra conexion y termina
scf
jp RETR_END
RETR_E2: print DCONREF_S ;Conexion de datos cerrada sin llegar
ld a,(DATA_CON)
call TCP_ABORT
call WAIT_REPLY ;a abrirse (probable perdida de con. a internet)
scf
jp RETR_END
RETR_EX: ld a,(DATA_CON) ;Respuesta inesperada al
call TCP_ABORT ;comando "PORT"
call R_UNEX
scf
;jp RETR_END
RETR_END: push af
ld a,(FILE_FH)
cp #FF
jr z,RETR_END2
ld a,(FILE_FH)
ld b,a
ld c,_CLOSE
call DO_DOS
ld a,#FF
ld (FILE_FH),a
RETR_END2: ;
ld a,(MUST_ASCII)
or a
jr z,RETR_END2!
ld a,(CONTROL_CON)
cp #FF
call nz,SILENT_BIN ;Restaura TYPE antiguo
RETR_END2!: pop af
ret
RETR_COM: dw 0
RETR_PAR: db 0
PUTR_PAR: equ RETR_PAR
MUST_ASCII: db 0
;--- SILENT_ASCII: Establece modo ASCII sin mostrar respuesta
; aunque VERBOSE este activado.
; Devuelve Cy=1 en caso de error.
SILENT_ASCII: ld a,(VERBOSE)
ld (OLD_VERBOSE),a
xor a
ld (VERBOSE),a
ld a,(TYPE) ;Primero pone tipo ASCII
ld (OLD_TYPE),a ;(no muestra el resultado del comando
call R_ASCII ;aunque VERBOSE este activado)
ld a,(OLD_VERBOSE)
ld (VERBOSE),a
ret
;--- SILENT_BIN: Restaura el modo de OLD_TYPE sin mostrar respuesta
; aunque VERBOSE este activado.
; Devuelve Cy=1 en caso de error.
SILENT_BIN: ld hl,SILENT_BIN2
push hl
ld a,(VERBOSE) ;Restaura TYPE antiguo
ld (OLD_VERBOSE),a
xor a
ld (VERBOSE),a
ld a,(OLD_TYPE)
or a
jp z,R_ASCII
jp R_BINARY
SILENT_BIN2: ld a,(OLD_VERBOSE)
ld (VERBOSE),a
ret
;--- MULTIPLE: Ejecuta un comando multiple (MGET, MPUT, MDELETE)
; - Ejecuta NLST silenciosamente y guarda el resultado
; en una lista
; - Para cada nombre de fichero, pregunta "Y,N,A,C?" si en
; modo PROMPT, y si la respuesta es "y", establece el nombre
; de fichero como segundo parametro en USER_COM_BUF y ejecuta
; la rutina (MUL_COM)
; - Al preguntar para cada fichero, muestra la cadena (MUL_STR)
MULTIPLE: call _MULTIPLE
push af
ld a,(MUL_LISTA)
ld ix,0
nesman 22
ld a,#FF
ld (MUL_LISTA),a
ld a,(OLD_VERBOSE_M)
ld (VERBOSE),a
pop af
ret
_MULTIPLE: ;--- Crea una lista y si no hay memoria, termina con error
ld a,4
ld (ABORT_STAT),a
ld a,(VERBOSE)
ld (OLD_VERBOSE_M),a
or a ; Lista interna
nesman 20
jr nc,MUL_OK1
print OUTOFM_S
ret
MUL_OK1: ld (MUL_LISTA),a
;--- Abre conexion, envia TYPE A,
; envia PORT, espera conexion OK, envia NLST
xor a
ld (VERBOSE),a
call SILENT_ASCII ;Pone modo ASCII si es necesario
jp c,MUL_END
ld a,#FF
ld (MUST_ASCII),a
call OPEN_DCON ;Abre conexion, envia PORT
jp c,MUL_END
ld hl,C_NLST ;Envia NLST con el 2o parametro
call SET_COMMAND
ld hl,USER_COM_BUF+1
ld de,SEND_COM_BUF+5
ld a,2
call EXTPAR
call nc,SET_SPACE
call APPEND_LF
call SEND_COM
;debug "1"
call WAIT_REPLY ;Espera respuesta, que ha de
;debug "2"
jp c,MUL_E ;ser 1yz
cp 1
jr z,MUL_WAIT
cp 4
jp z,MUL_E
cp 5
jp z,MUL_E
jp MUL_EX
MUL_WAIT: ;debug "3"
call HALT ;Espera a que la conexion este
ld a,(DATA_CON) ;o ESTABLISHED o cerrandose
call TCP_STATUS ;(ya se han recibido todos los datos)
or a
jp z,MUL_E2
cp 4
jr c,MUL_WAIT
;debug "4"
xor a
ld hl,DATA_BUF
;--- Va cogiendo Los nombres de fichero y los
; inserta en la lista
MUL_NLST_LOOP: push af,hl
MUL_NLST_LOOP2: call GET_DDATA
jr c,MUL_NODATA
cp 10
jr z,MUL_NLST_LOOP2
cp 13
jr z,MUL_NEXT_NAME
pop hl
ld (hl),a
inc hl
pop af
inc a
jr MUL_NLST_LOOP
MUL_NEXT_NAME: ;Nombre completo obtenido, HL apunta a su direccion
;y A contiene su longitud (en la pila)
pop hl,af ;Inserta nombre en la lista
ld l,a
ld h,0
ld a,(MUL_LISTA)
ld ix,0
ld b,3
ld iy,DO_DOS
nesman 24
jp c,MUL_MEME
ld hl,DO_DOS
ld a,0
jr MUL_NLST_LOOP
;--- No se pueden coger mas datos: esperamos o
; pasamos a tratar los nombres de fichero recibidos
MUL_NODATA: pop hl,bc
push af
ld (MUL_NAME_PNT),hl
ld a,b
ld (MUL_NAME_SIZE),a
pop af
;cp 5 ;Si no hay datos pero la conexion sigue ESTABLISHED
;jp nz,MUL_E2 ;(puede recibir mas datos), sigue esperando
ld a,(DATA_CON)
call TCP_STATUS
cp 4
jr nz,MUL_NODATA2
call HALT ;Esperamos una interrupcion y
ld a,(MUL_NAME_SIZE) ;volvemos a intentarlo
ld hl,(MUL_NAME_PNT)
jr MUL_NLST_LOOP
MUL_NODATA2: ld a,(DATA_CON)
call TCP_CLOSE ;TCP_ABORT
ld a,#FF
ld (DATA_CON),a
call WAIT_REPLY ;Conexion cerrada legalmente: la cerramos nosotros,
jp c,MUL_END ;espera respuesta de la conexion de control,
cp 2 ;y termina
jp z,MUL_OK_LIST
cp 4
jp z,MUL_END
cp 5
jp z,MUL_END
print UNEXPEC_S
scf
jp MUL_END
;--- Ya tenemos la lista completa de ficheros,
; ahora ejecutamos el comando adecuado
MUL_OK_LIST: xor a
ld (MUST_ASCII),a
call SILENT_BIN
jp c,MUL_END
ld a,(OLD_VERBOSE_M)
ld (VERBOSE),a
;>>> MPUT salta aqui
MULTIPLE2: ld a,(PROMPT)
cpl
ld (ALLFILES),a
;--- Obtiene un nombre de fichero y
; pregunta si se ha de actuar sobre el,
; solo si ALLFILES=#FF
MUL_FILE_LOOP: ld a,(MUL_LISTA)
ld ix,0
ld b,1
ld h,3
ld iy,DATA_BUF
nesman 25
ret c
ld hl,DATA_BUF
add hl,bc
ld (hl),0
inc bc
inc bc
ld a,c
ld (USER_COM_BUF+1),a
ld a,(ALLFILES)
or a
jr nz,MUL_DO_FILE
ld de,(MUL_STR)
ld c,_STROUT
call DO_DOS
printz DATA_BUF
call ASK_YNAC
cp 3 ;Cancel?
push af
ld c,_STROUT
ld de,OPCAN_S
call z,DO_DOS
pop af
ret z
cp 1 ;No?
jr z,MUL_FILE_LOOP
or a ;Yes?
jr z,MUL_DO_FILE
ld a,#FF ;All
ld (ALLFILES),a
MUL_DO_FILE: ld hl," X"
ld (USER_COM_BUF+2),hl
ld hl,DATA_BUF
ld de,USER_COM_BUF+4
ld bc,252
ldir
ld hl,(MUL_COM)
call JP_HL
jp MUL_FILE_LOOP
;--- Rutinas de terminacion
MUL_E: ld a,(DATA_CON) ;Error del comando "PORT":
call TCP_ABORT ;cierra conexion y termina
scf
jp MUL_END
MUL_E2: print DCONREF_S ;Conexion de datos cerrada sin llegar
ld a,(DATA_CON)
call TCP_ABORT
call WAIT_REPLY ;a abrirse (probable perdida de con. a internet)
scf
jp MUL_END
MUL_EX: ld a,(DATA_CON) ;Respuesta inesperada al
call TCP_ABORT ;comando "PORT"
call R_UNEX
scf
jr MUL_END
MUL_MEME: print OUTOFM_S
ld a,(DATA_CON)
call TCP_ABORT
call WAIT_REPLY
;jr MUL_END
MUL_END: push af
ld a,(MUST_ASCII)
or a
jr z,MUL_END2!
ld a,(CONTROL_CON)
cp #FF
call nz,SILENT_BIN ;Restaura TYPE antiguo
MUL_END2!: pop af
ret
;--- Variables
MUL_LISTA: db 0
MUL_NAME_SIZE: db 0
MUL_NAME_PNT: dw 0
OLD_VERBOSE_M: db 0
MUL_STR: dw 0
MUL_COM: dw 0
JP_HL: jp (hl)
;--- R_ABORT: Rutina de tratamiento de CTRL-C o CTRL-STOP,
; o de "abort" en errores de disco
; La variable ABORT_STAT ha de contener uno de estos valores:
; 0: Inicializando el programa, o ejecutando TERM
; 1: Bucle principal, no hay conexion
; 2: Bucle principal, hay conexion, no se ejecuta ningun comando
; 3: Se esta ejecutando un comando local (configuracion, acceso a disco)
; 4: Se esta ejecutando un comando FTP, con o sin conexion de datos
; 5: Se esta ejecutando CLOSE o QUIT
R_ABORT: pop hl ;Para ajustar la pila
push af
xor a
ld (LDIR_EXE),a
ld (PUT_EXE),a
ld a,(MUL_LISTA)
ld ix,0
nesman 22
ld a,#FF
ld (MUL_LISTA),a
ld a,(HOOKING) ;Restauramos gancho CHPUT
or a ;si es necesario
jr z,AB_NOHOOK
xor a
ld (HOOKING),a
ld hl,HOOK_OLD
ld de,H_CHPH
ld bc,5
ldir
AB_NOHOOK: pop af
;--- Primero comprobamos si es un error de disco,
; en ese caso simplemente terminamos
cp #9D ;Codigo de "disk operation aborted"
jr nz,R_AB_NODISK
ld a,b
or a
ret
;--- Imprimimos "CTRL-C pressed" o "CTRL-STOP pressed"
R_AB_NODISK: ld de,CTRLC_S
cp #9E ;Codigo de "CTRL-C pressed"
jr z,R_AB_PRINT
ld de,CTRLS_S
cp #9F ;Codigo de "CTRL-STOP pressed"
jr z,R_AB_PRINT
ld de,UNKE_S ;Error desconocido
R_AB_PRINT: ld c,_STROUT
call DO_DOS
ld a,(ABORT_STAT)
;--- 0: No hacemos nada (ignoramos CTRL-C/STOP)
R_ABORT_0: or a
jr nz,R_ABORT_1
ret
;--- 1: Salimos del programa
R_ABORT_1: cp 1
jr nz,R_ABORT_2
print EXITING_S
jp TERM
;--- 2: Ejecutamos CLOSE
R_ABORT_2: cp 2
jr nz,R_ABORT_3
print DISCONN_S
jp R_CLOSE
;--- 3: Saltamos al bucle principal
R_ABORT_3: cp 3
jr nz,R_ABORT_4
print ABCOMM_S
call CLOSE_FILE
jp MAIN_LOOP
;--- 4: Enviamos ABORT si no hay datos pendientes
; en la conexion de control
R_ABORT_4: cp 4
jr nz,R_ABORT_5
ld a,5
ld (ABORT_STAT),a
call CLOSE_FILE
print ABCOMM_S
ld a,(CONTROL_CON) ;Hay datos en conexion de control?
call TCP_STATUS ;Entonces recogerlos y no
or a
jp z,MAIN_LOOP ;enviar ABORT
ld a,h
or l
jr z,R_AB4_1
call WAIT_REPLY
jr R_AB4_2
R_AB4_1: ;ld hl,#F4FF ;Envia comando telnet IP
;ld (SEND_COM_BUF),hl
;ld hl,SEND_COM_BUF
;ld bc,2
;scf
;ld a,(CONTROL_CON)
;call TCP_SEND
;ld hl,#F2FF ;Envia secuencia telnet SYNCH
;ld (SEND_COM_BUF),hl
;ld hl,SEND_COM_BUF
;ld bc,2
;ld d,2 ;??? (si es datos urgentes, vas dao...)
;ld a,(CONTROL_CON)
;call TCP_SEND
ld hl,C_ABOR
call SET_COMMAND
call APPEND_LF
call AUTOMATA_NO1
R_AB4_2: ld a,(DATA_CON)
cp #FF
jp z,MAIN_LOOP
call TCP_ABORT
ld a,#FF
ld (DATA_CON),a
jp MAIN_LOOP
;--- 5: Abortamos conexion de control
R_ABORT_5: ld a,(DATA_CON)
cp #FF
jr z,R_ABORT_52
print ABDCON_S
ld a,(DATA_CON)
call TCP_ABORT
ld a,#FF
ld (DATA_CON),a
ret
R_ABORT_52: print ABCCON_S
call CLOSE_FILE
ld a,(DATA_CON)
cp #FF
jr z,R_ABORT_53
call TCP_ABORT
R_ABORT_53: ld a,(CONTROL_CON)
call TCP_ABORT
jp MAIN_LOOP
;--- Automata del protocolo de identificacion
; Espera datos de la conexion IDENT_CON,
; y los devuelve seguidos de IDENT_S
IDENT_AUTOM: push af,bc,de,hl
call _IDENT_AUTOM
pop hl,de,bc,af
ret
_IDENT_AUTOM: ld a,(IDENT_CON)
cp #FF
ret z
call TCP_STATUS
or a
jr nz,IDENT_AUT1
ld a,#FF
ld (IDENT_CON),a
ret
IDENT_AUT1: cp 4
ret c
ld a,(IDENT_CON) ;Recoge datos
ld de,DATA_BUF
ld bc,256
call TCP_RCV
ret z
push bc
ld hl,DATA_BUF ;Compone cadena de respuesta
add hl,bc
ex de,hl
ld hl,IDENT_S
ld bc,IDENT_LEN
ldir
pop bc ;Envia respuesta
ld hl,IDENT_LEN
add hl,bc
push hl
pop bc
ld hl,DATA_BUF
ld a,(IDENT_CON)
scf
call TCP_SEND
ret
;--- Codigo del gancho para CHPUT para enmascaramiento de password
; Imprime todos los caracteres como "*",
; excepto espacios, caracteres de control (<32 y 127)
; y secuencias de escape
HOOK_CODE0: pop hl,bc ;B=Caracter a imprimir
ld a,(ESC_CHAR)
or a
jr z,HOOK_C0_NORMAL
ld c,a ;C=Caracter en ESC_CHAR
;Tratamiento de una secuencia de escape
cp 27 ;El caracter anterior era ESC:
ld a,b ;Ponemos en ESC_CHAR el caracter
jr z,HOOK_C0_SETESC ;actual e imprimimos el ESC
ld a,c
cp 1 ;"1": secuencia "ESC Y n m"
ld a,0 ;y el caracter actual es "m":
jr z,HOOK_C0_SETESC ;lo imprimimos
ld a,c
cp "Y" ;Secuencia "ESC Y n m" y el
ld a,1 ;caracter actual es "n":
jr z,HOOK_C0_SETESC ;lo imprimimos
ld a,c
and %11111110 ;Secuencia "ESC x n" o "ESC y n"
cp "x" ;y el caracter actual es "n":
ld a,0 ;lo imprimimos
jr z,HOOK_C0_SETESC
ld a,b ;Otra secuencia "ESC m" ya completa
jr HOOK_C0_NORMAL
HOOK_C0_SETESC: ld (ESC_CHAR),a ;ESC_CHAR=0 si no estamos
ld a,b ;en una secuencia de escape
jr HOOK_C0_OK
;Tratamiento normal (no estamos en secuencia ESC)
HOOK_C0_NORMAL: ld a,b
cp 27
jr nz,HOOK_C0_NORM2
ld (ESC_CHAR),a
jr HOOK_C0_OK
HOOK_C0_NORM2: cp 33
jr c,HOOK_C0_OK
cp 127
jr z,HOOK_C0_OK
ld a,"*"
HOOK_C0_OK: push af,hl
HOOK_CODE0_END: ;
;--- Rutinas para establecer y resetear el gancho
SET_HOOK: push af,bc,de,hl
di
ld a,#C3
ld (H_CHPH),a
ld hl,HOOK_CODE
ld (H_CHPH+1),hl
ld a,#FF
ld (HOOKING),a
ei
pop hl,de,bc,af
ret
RESET_HOOK: push af,bc,de,hl
di
ld hl,HOOK_OLD
ld de,H_CHPH
ld bc,5
ldir
ei
pop hl,de,bc,af
ret
;--- Code to switch TCP/IP implementation on page 1, if necessary
SET_UNAPI:
ld a,(UNAPI_IS_SET)
or a
ret nz
dec a
ld (UNAPI_IS_SET),a
UNAPI_SLOT: ld a,0
ld h,#40
call ENASLT
UNAPI_SEG: ld a,0
jp PUT_P1
CALL_UNAPI: ex af,af'
exx
call SET_UNAPI
ei
ex af,af'
exx
DO_UNAPI: jp 0
;--- Code to call a DOS function
DO_DOS:
ex af,af'
xor a
ld (UNAPI_IS_SET),a
ex af,af'
jp 5
;>>> These rouines invoke the TCP related TCP/IP UNAPI <<<
;>>> routines accepting the old INL input parameters <<<
;--- TCP_OPEN: Open a new TCP connection.
;Input: A = 0 for active, 1 for passive
; L.H.E.D = remote IP (0.0.0.0 for passive with unespecified remote socket)
; IX = Remote port (ignored if IP=0.0.0.0)
; IY = Local port (#FFFF for random between 16384 and 32767)
; BC = Usuer timeout in seconds
; (1-1080, 0 for 3 minutes, #FFFF for infinite)
;Output: Cy=0 if OK, 1 if error
; If OK: A = Connection number
; If error: A = UNAPI Error code
TCP_OPEN:
ld (TCP_INFO_BLOCK),hl
ld (TCP_INFO_BLOCK+2),de
ld (TCP_INFO_BLOCK+4),ix
ld (TCP_INFO_BLOCK+6),iy
ld (TCP_INFO_BLOCK+8),bc
and 1
ld (TCP_INFO_BLOCK+10),a
ld a,TCPIP_TCP_OPEN
ld hl,TCP_INFO_BLOCK
call CALL_UNAPI
or a
scf
ret nz
ld a,b
or a
ret
TCP_INFO_BLOCK: ds 11
;--- TCP_CLOSE: Close a TCP connection
;Input: A = Connection number
;Output: Cy=1 if error, then A=UNAPI error code
TCP_CLOSE:
ld b,a
ld a,TCPIP_TCP_CLOSE
call CALL_UNAPI
or a
scf
ret nz
or a
ret
;--- TCP_ABORT: Abort a TCP connection
;Input: A = Connection number
;Output: Cy=1 if error, then A=UNAPI error code
TCP_ABORT:
ld b,a
ld a,TCPIP_TCP_ABORT
call CALL_UNAPI
or a
scf
ret nz
or a
ret
;--- TCP_STATUS: Obtain the state of a TCP connection
;Input: A = Connection number
;Output: Cy=1 if error, then A=UNAPI error code
; Cy=0 if OK, and:
; A = Connection state
; HL = Available received bytes
; DE = Available space for outgoing bytes
; BC = Bytes in retransmission queue
; IX = Connection TCB address in data segment (NOT available here)
; (If A=0, B=LAST_CLOSE)
TCP_STATUS:
ld b,a
ld a,TCPIP_TCP_STATE
ld hl,0
call CALL_UNAPI
or a
jr z,TCP_STATUS_2
ld b,c
xor a ;Convert errors to "connection closed"
ret
TCP_STATUS_2:
ld a,b
push ix
pop de
ld ix,0
ld bc,0
or a
ret
;--- TCP_SEND: Send data to a TCP connection
;Input: A = Connection number
; HL = Data address in TPA
; BC = Data length
; Cy=1 for PUSH
;Output: Cy=1 if error, then A=UNAPI error code
TCP_SEND:
push hl,bc
ld b,a
rla
and 1
ld c,a
pop hl,de
ld a,TCPIP_TCP_SEND
call CALL_UNAPI
or a
scf
ret nz
or a
ret
;--- TCP_RCV: Obtain data from a TCP connection
;Input: A = Connection number
; DE = Destination address for data
; BC = Data length
;Output: Cy=1 if error, then A=UNAPI error code
; 1: Invalid connection number
; 2: Connection is closed
; BC = Actual obtained data length
; Z = 1 if BC=0
TCP_RCV:
push af,bc
pop hl,bc
ld a,TCPIP_TCP_RCV
call CALL_UNAPI
or a
scf
ret nz
ld a,b
or c
ld a,0
ret
;*************************************
;*** ***
;*** DATOS, VARIABLES, CADENAS ***
;*** ***
;*************************************
DATA:
org #8000
DATA_START:
;--- Variables
IP_LOCAL: ds 4
IP_REMOTE: ds 4
PORT_REMOTE_C: dw 21
PORT_REMOTE_I: dw 0 ;Ident: el puerto local sera el 113
PORT_REMOTE_D: dw 0
HAY_NMAN: db 0
DATA_CON: db #FF
CONTROL_CON: db #FF
IDENT_CON: db #FF
VERBOSE: db #FF
XCOM: db 0
ES_XCOM: db 0
SAVESP: dw 0
TYPE: db 0 ;0 para ASCII, 1 para binario
DEBUG: db 0
BELL: db 0
HASH: db 0
PROMPT: db #FF
PAUSE: db 0
PASSIVE: db 0
OLD_TYPE: db 0
OLD_VERBOSE: db 0
PORT_C_PNT: dw C_PORT+5 ;Puntero a la parte "puerto" de PORT_C
FILE_FH: db #FF
FILES_QUEUE: db #FF ;Cola para guardar los nombres de fichero
QUITTING: db 0 ;#FF cuando se esta cerrando/saliendo
ALLFILES: db 0
ALSO_RONLY: db 0
LINE_COUNT: db 0
BYTE_COUNT: db 0
INS_IX: dw 0
ABORT_STAT: db 0
LAST_REPLY: db 0
MSS: dw 448 ;Valor fijo
HOOKING: db 0 ;#FF cuando el gancho CHPUT esta parcheado
UNAPI_IS_SET: db 0 ;#FF when UNAPI slot/seg is switched on page 1
;--- Pseudo-TCBs
;Control connection
IP_REMOTE_C: db 0,0,0,0
;PORT_REMOTE_C: dw 21
;Data connection
IP_REMOTE_D: db 0,0,0,0
;PORT_REMOTE_D: dw 0
;db #FF ;Control connection is passive
;Ident protocol connection
;PORT_REMOTE_I: dw 0
;dw 113
;db #FF ;Is passive
;--- Cadenas de texto (informacion y errores)
PRESEN_S: db 13,10,"FTP client for the TCP/IP UNAPI 1.0",13,10
db "By Konamiman, 4/2010",13,10,10
TYPEHELP_S: db "Type ",34,"HELP",34," or ",34,"?",34," at any time to obtain help.",13,10,10,"$"
NONMAN_S: db 13,10,"*** This command needs NestorMan 1.21 or higher to be installed",13,10,13,10,"$"
ERROR_S: db 13,10,"*** ERROR $"
UNKERR_S: db "Unknown error",13,10,"$"
UNKCOM_S: db "Unknown command",13,10,"$"
INVPAR_S: db "Invalid or missing parameter(s) for command",13,10,"$"
NOHELP_S: db " is not a command implemented in this application",13,10,"$"
WHENDNS_S: db "when resolving server name:",13,10," $"
WHENOPEN_S: db "when creating or accessing local file:",13,10," $"
WHENCONN_S: db "when connecting to the server:",13,10," $"
WHENOPEND_S: db "when opening data connection:",13,10," $"
NOIMP_S: db "Command not implemented in this version of the application",13,10,"$"
CONNECTING_S: db "Connecting (press ESC to cancel)... $"
CONLOST_S: db 13,10,"*** ERROR: Connection to FTP server lost",13,10,10,"$"
NOMEM_S: db 13,10,"*** ERROR: Not enough memory to enqueue data",13,10,10,"$"
UNEXPEC_S: db 13,10,"*** ERROR: Unexpected reply from FTP server",13,10,10,"$"
CONREF_S: db 13,10,"*** ERROR: Connection refused",13,10,10,"$"
DCONREF_S: db 13,10,"*** ERROR: Unexpected data connection loss",13,10,10,"$"
OUTOFM_S: db 13,10,"*** ERROR: Out of memory",13,10,"$"
DISKERR_S: db 13,10,"*** Disk error:",13,10," $"
CTRLC_S: db 13,10,"*** CTRL-C pressed: $"
CTRLS_S: db 13,10,"*** CTRL-STOP pressed: $"
UNKE_S: db 13,10,"*** Unknown error!: $"
CANCELLED_S: db 13,10,"*** Connection cancelled",13,10,"$"
EXITING_S: db "Exiting FTP application",13,10,"$"
DISCONN_S: db "Disconnecting from FTP server",13,10,"$"
ABCOMM_S: db "Aborting command execution",13,10,"$"
ABCCON_S: db "Aborting FTP server connection",13,10,"$"
ABDCON_S: db "Aborting data connection",13,10,"$"
FLUSHED_S: db "Flushing: $"
ANONYMOUS_S: db "anonymous",0
NONE_S: db "none",0
ENV_USER: db "FTP_USER",0
ENV_PASSWORD: db "FTP_PASSWORD",0
ENV_ACCOUNT: db "FTP_ACCOUNT",0
FTP_S: db 13,"ftp>$"
FLECHA_S: db 13,"--->$"
ON_S: db "ON$"
OFF_S: db "OFF$"
OK_S: db "OK",13,10,"$"
TURNED_S: db " mode turned $"
TYPESET_S: db "Type set to $"
HASH_M_S: db "Hash$"
DEBUG_M_S: db "Debug$"
VERBOSE_M_S: db "Verbose$"
PROMPT_M_S: db "Prompt$"
BELL_M_S: db "Bell$"
PAUSE_M_S: db "Pause$"
PASSIVE_M_S: db "Passive$"
XCOM_M_S: db "X Commands$"
HASH_S: db "Hash: $"
DEBUG_S: db "Debug: $"
VERBOSE_S: db "Verbose: $"
PROMPT_S: db "Prompt: $"
PASSIVE_S: db "Passive: $"
PAUSE_S: db "Pause: $"
BELL_S: db "Bell: $"
TYPE_S: db "Type: $"
XCOM_S: db "X Commands: $"
ASCII_S: db "ASCII",13,10,"$"
BINARY_S: db "Binary",13,10,"$"
DISCON_S: db 13,"Disconnected",13,10,"$"
CONNTO_S: db 13,10,"Connected to: $"
USER._S: db "User ($"
PASSWORD._S: db "Password ($"
CIERRAPAR_S: db "): $"
ACCOUNT._S: db "Account ($"
CURPATH_S: db "Current local path: $"
PATHCHAN_S: db "Local path changed to: $"
DIROF_S: db 13,10,"Directory of $"
ESDIR_S: db " $"
FFOUND_S: db " file(s) found",13,10,"$"
DFOUND_S: db " directory(es) found",13,10,"$"
KFREE_S: db "K free on drive $"
DELETE_S: db "Delete local file $"
RECEIVE_S: db "Receive file $"
DELETER_S: db "Delete file $"
SENDFILE_S: db "Send file $"
OPCOMP_S: db "Operation completed",13,10,"$"
OPCAN_S: db "Operation cancelled, remaining files skipped",13,10,"$"
YNAC_S: db " (y,n,a,c)? $"
ALCON_S: db "Already connected, CLOSE first",13,10,"$"
PRESS_S: db "--- Press any key to continue (",34,"P",34," to turn off pause mode) --- $"
IDENT_S: db ":USERID:OTHER:MSX-DOS 2 running FTP for the TCP/IP UNAPI"
IDENT_S_END: ;
IDENT_LEN: equ IDENT_S_END-IDENT_S
;--- Cadenas de ayuda
BIGHELP_H: db 13,10,"Available commands:",13,10,10
db "? dir ls recv",13,10
db "! disconnect lshow remotehelp",13,10
db "append get mdelete rename",13,10
db "ascii hash mget rmdir",13,10
db "bell help mkdir send",13,10
db "binary lcd mput show",13,10
db "bye ldelete open status",13,10
db "cd ldir passive type",13,10
db "cdup literal pause user",13,10
db "close lmkdir prompt verbose",13,10
db "debug lrename put xcommands",13,10
db "delete lrmdir quote",13,10
db 10,"Program execution: FTP [| []]",13,10
db "On ",34,"multiple",34," commands, ",34,"(y,n,a,c)?",34," means ",34,"(Yes, No, yes to All, Cancel)?",34,13,10,10
db "Default values for the login sequence can be set up in the environment",13,10
db "variables FTP_USER, FTP_PASSWORD and FTP_ACCOUNT",13,10
db 10,"$"
H_APPEND: db "APPEND ",13,10
db "Appends the content of the local file",13,10
db "at the end of the remote file.",13,10,"$"
H_ASCII: db "ASCII",13,10
db "Sets the transfer mode to ASCII (same as TYPE A).",13,10,"$"
H_BELL: db "BELL",13,10
db "Turns on/off the bell mode. In this mode, a BEEP will sound",13,10
db "when a command execution is complete.",13,10,"$"
H_BINARY: db "BINARY",13,10
db "Sets the transfer mode to binary (same as TYPE I).",13,10,"$"
H_CD: db "CD []",13,10
db "Changes to the specified remote directory.",13,10
db "If no directory is specified, shows the current one (same as ",34,"PWD",34,").",13,10,"$"
H_CDUP: db "CDUP",13,10
db "Changes to the parent directory (usually same as CD ..)",13,10,"$"
H_CLOSE: db "CLOSE|DISCONNECT",13,10
db "Closes connection to FTP server.",13,10,"$"
H_DEBUG: db "DEBUG",13,10
db "Turns on/off the debug mode. In this mode, all commands sent",13,10
db "to the FTP server will be printed on the screen.",13,10,"$"
H_DELETE: db "DELETE ",13,10
db "Deletes the specified remote file.",13,10,"$"
H_DIR: db "DIR [ []]",13,10
db "Shows the detailed contents of the remote directory",13,10
db "according to the specified mask (all files if omitted).",13,10
db "If is specified, information will be stored on",13,10
db "the specified file instead of being shown on the screen.",13,10
db "Mask specification is mandatory when specifying ,",13,10
db "use ",34,".",34," to specify ",34,"all files",34,".",13,10,"$"
H_FLUSH: ;db "FLUSH",13,10
;db "Flushes any data received from the FTP server but not yet",13,10
;db "consummed by the application. This type of data appears tipically",13,10
;db "when aborting commands with CTRL-C. Use FLUSH when the application",13,10
;db "starts to act incorrectly or gives strange errors.",13,10,"$"
H_GET: db "GET|RETR []",13,10
db "Obtains the specified remote filename.",13,10,"$"
H_HASH db "HASH",13,10
db "Turns on/off the hash mode. In this mode, every time that",13,10
db "new data arrives from the data connection, a ",34,"#",34," symbol",13,10
db "is printed on the screen.",13,10,"$"
H_HELP: db "HELP|? []",13,10
db "Shows help on available commands.",13,10,"$"
H_LCD: db "LCD [:][]",13,10
db "Changes to the specified local drive and/or directory.",13,10
db "If no parameter is specified, shows the current drive and directory.",13,10,"$"
H_LDELETE: db "LDELETE [R]",13,10
db "Deletes the specified local file(s).",13,10
db "With ",34,"R",34," deletes also read-only file(s).",13,10,"$"
H_LDIR: db "LDIR [\][]",13,10
db "Shows the contents of the specified local directory (current one if omitted)",13,10
db "according to the specified mask (all files if omitted).",13,10,"$"
H_LITERAL: db "LITERAL|QUOTE ",13,10
db "!",13,10
db "Sends a raw command to the FTP server.",13,10
db "When using ",34,"!",34,", do not put an space between it and the command",13,10
db "(for example: !NOOP)",13,10,"$"
H_LMKDIR: db "LMKDIR ",13,10
db "Creates the specified local directory.",13,10,"$"
H_LRENAME: db "LRENAME ",13,10
db "Renames the specified local file(s).",13,10,"$"
H_LRMDIR: db "LRMDIR ",13,10
db "Removes the specified local directory.",13,10,"$"
H_LS: db "LS [ []]",13,10
db "Shows the contents of the remote directory (filenames only)",13,10
db "according to the specified mask (all files if omitted).",13,10
db "If is specified, information will be stored on",13,10
db "the specified file instead of being shown on the screen.",13,10
db "Mask specification is mandatory when specifying ,",13,10
db "use ",34,".",34," to specify ",34,"all files",34,".",13,10,"$"
H_LSHOW: db "LSHOW ",13,10
db "Shows the contents of the specified local file",13,10
db "(which is assumed to be a text file).",13,10,"$"
H_MDELETE: db "MDELETE ",13,10
db "Deletes multiple remote files, according to the mask.",13,10
db "This command needs NestorMan 1.21 or higher to be installed.",13,10,"$"
H_MGET: db "MGET ",13,10
db "Obtains multiple remote files.",13,10
db "This command needs NestorMan 1.21 or higher to be installed.",13,10,"$"
H_MKDIR: db "MKDIR ",13,10
db "Creates the specified remote directory.",13,10,"$"
H_MPUT: db "MPUT ",13,10
db "Sends multiple files to the FTP server.",13,10
db "This command needs NestorMan 1.21 or higher to be installed.",13,10,"$"
H_OPEN: db "OPEN | []",13,10
db "Opens a connection to the specified FTP server.",13,10
db "Default port number is 21.",13,10,"$"
H_PASSIVE: db "PASSIVE",13,10
db "Turns on/off the passive mode. In this mode, the application opens",13,10
db "actively the data connections, instead of waiting for the server to do it.",13,10,"$"
H_PAUSE: db "PAUSE",13,10
db "Turns on/off the pause mode. In this mode, all commands involving",13,10
db "data output to the screen (LIST, LS, SHOW and LSHOW) make a pause",13,10
db "and ask for key pressing after a complete screen has been displayed.",13,10,"$"
H_PROMPT: db "PROMPT",13,10
db "Turns on/off the prompt mode. In this mode, all commands involving",13,10
db "multiple files processing (MGET, MPUT, MDELETE, LDELETE) ask the user",13,10
db "for confirmation on each file.",13,10,"$"
H_PWD: db "PWD",13,10
db "Shows current remote directory",13,10
db "(same as ",34,"CD",34," without parameters).",13,10,"$"
H_REMOTEHELP: db "REMOTEHELP []",13,10
db "Requests and shows help from the FTP server.",13,10,"$"
H_RENAME: db "RENAME ",13,10
db "Renames the specified remote file.",13,10,"$"
H_RMDIR: db "RMDIR ",13,10
db "Removes the specified remote directory.",13,10,"$"
H_SEND: db "PUT|SEND ",13,10
db "Sends the specified local file to the FTP server.",13,10,"$"
H_SHOW: db "SHOW ",13,10
db "Shows the contents of the specified remote file",13,10
db "(which is assumed to be a text file).",13,10,"$"
H_STATUS: db "STATUS",13,10
db "Shows the current status of the application.",13,10,"$"
H_TYPE: db "TYPE A|I"
db "Sets the file transfer type to ASCII (A) or binary (I).",13,10
db "Commands ASCII and BINARY can be used for the same purpose.",13,10,"$"
H_QUIT: db "BYE|QUIT",13,10
db "Closes connection to FTP server and exits application.",13,10,"$"
H_USER: db "USER ",13,10
db "(Re)starts the login procedure on the FTP server.",13,10
db "Default values por user, password and account information",13,10
db "can be set up in the environment variables FTP_USER,",13,10
db "FTP_PASSWORD and FTP_ACCOUNT respectively.",13,10,"$"
H_VERBOSE: db "VERBOSE",13,10
db "Turns on/off the verbose mode. In this mode, all replies received",13,10
db "from the server are printed on the screen (error messages are always printed).",13,10,"$"
H_XCOMMANDS: db "XCOMMANDS",13,10
db "Turns on/off the ",34,"X",34," commands mode. In this mode, FTP commands",13,10
db "XMKD, XRMD, XPWD, XCUP and XCWD are sent to the FTP server",13,10
db "instead of MKD, RMD, PWD, CDUP and CWD, respectively.",13,10,"$"
;--- Comandos para enviar al servidor FTP
C_ABOR: db "ABOR",0
C_ACCT: db "ACCT",0
C_APPE: db "APPE",0
C_CDUP: db "CDUP",0
C_DELE: db "DELE",0
C_CWD: db "CWD",0,0
C_RETR: db "RETR",0
C_HELP: db "HELP",0
C_LIST: db "LIST",0
C_MKD: db "MKD",0,0
C_NLST: db "NLST",0
C_PASS: db "PASS",0
C_PASV: db "PASV",0
C_PORT: db "PORT 000,000,000,000,000,000",13,10
C_PWD: db "PWD",0,0
C_QUIT: db "QUIT",0
C_RMD: db "RMD",0,0
C_RNFR: db "RNFR",0
C_RNTO: db "RNTO",0
C_STOR: db "STOR",0
C_TYPE: db "TYPE",0
C_USER: db "USER",0
C_XCUP: db "XCUP",0
;--- Tabla de comandos de usuario y direccion de su rutina y texto de ayuda
COM_ENTRY: macro @c ;Entrada para comando normal
db "@c",0
dw R_@c
dw H_@c
endm
COM_ENTRY2: macro @c,@x ;Entrada para alias
db "@c",0
dw R_@x
dw H_@x
endm
COMMAND_TABLE: ;
COM_ENTRY2 ?,HELP
COM_ENTRY2 !,LITERAL
COM_ENTRY APPEND
COM_ENTRY ASCII
COM_ENTRY BELL
COM_ENTRY2 BYE,QUIT
COM_ENTRY BINARY
COM_ENTRY CD
COM_ENTRY CDUP
COM_ENTRY CLOSE
COM_ENTRY DEBUG
COM_ENTRY DELETE
COM_ENTRY DIR
COM_ENTRY2 DISCONNECT,CLOSE
;COM_ENTRY FLUSH
COM_ENTRY GET
COM_ENTRY HASH
COM_ENTRY HELP
COM_ENTRY LCD
COM_ENTRY LDELETE
COM_ENTRY LDIR
COM_ENTRY LITERAL
COM_ENTRY LMKDIR
COM_ENTRY LRENAME
COM_ENTRY LRMDIR
COM_ENTRY LS
COM_ENTRY LSHOW
COM_ENTRY MDELETE
COM_ENTRY MGET
COM_ENTRY MKDIR
COM_ENTRY MPUT
COM_ENTRY OPEN
COM_ENTRY PASSIVE
COM_ENTRY PROMPT
COM_ENTRY PAUSE
COM_ENTRY2 PUT,SEND
COM_ENTRY PWD
COM_ENTRY QUIT
COM_ENTRY2 QUOTE,LITERAL
COM_ENTRY2 RECV,GET
COM_ENTRY REMOTEHELP
COM_ENTRY RENAME
COM_ENTRY RMDIR
COM_ENTRY SEND
COM_ENTRY SHOW
COM_ENTRY STATUS
COM_ENTRY TYPE
COM_ENTRY USER
COM_ENTRY VERBOSE
COM_ENTRY XCOMMANDS
db 0
;--- Tabla de errores DNS
DNSERRS_T: db 1,"Query format error$"
db 2,"Server failure$"
db 3,"Name error (unknwon host)$"
db 4,"Query type not implemented$"
db 5,"Query refused$"
db 6,"DNS error 6$"
db 7,"DNS error 7$"
db 8,"DNS error 8$"
db 9,"DNS error 9$"
db 10,"DNS error 10$"
db 11,"DNS error 11$"
db 12,"DNS error 12$"
db 13,"DNS error 13$"
db 14,"DNS error 14$"
db 15,"DNS error 15$"
db 16,"Can't get a reply from DNS$"
db 17,"Operation timeout expired$"
db 18,"Query aborted$"
db 19,"Connection lost$"
db 20,"DNS did not give neither a reply nor a pointer to another DNS$"
db 21,"Answer is truncated$"
db 0
DNSQERRS_T: db ERR_NO_NETWORK,"No network connection$"
db ERR_NO_DNS,"No DNS servers available$"
db ERR_NOT_IMP,"This TCP/IP UNAPI implementation does not support name resolution.",13,10
db "An IP address must be specified instead.$"
db 0
;--- Tabla de errores al abrir una conexion
OPENERR_T: db ERR_NO_FREE_CONN,"Too many TCP connections opened$"
db ERR_NO_NETWORK,"No network connection found$"
db ERR_CONN_EXISTS,"Connection already exists, try another local port number$"
db ERR_INV_PARAM,"Invalid parameter when opening connection$"
db 0
db 0
DATA_END:
;--- Bufer para el comando tecleado por el usuario
USER_COM_BUF: equ DATA_END
;--- Bufer para el primer comando a enviar
SEND_COM_BUF: equ USER_COM_BUF+270
;--- Bufer para el nombre del servidor
SERVER_NAME: equ SEND_COM_BUF+270
;--- Bufer para la respuesta del servidor
RESPONSE_BUF: equ SERVER_NAME+257
;--- Bufer para examinar el comando del usuario
PARSE_BUF: equ RESPONSE_BUF+257
;--- Bufer para GET_DATA
GDATA_CBUF: equ PARSE_BUF+257
GDATA_DBUF: equ GDATA_CBUF+257
;--- Bufer para coger datos de la conexion de idem
DATA_BUF: equ GDATA_DBUF+257
;--- Buferes para el nombre de usuario y password por defecto
DEF_USER: equ DATA_BUF+1510
DEF_PASSWORD: equ DEF_USER+257
DEF_ACCOUNT: equ DEF_PASSWORD+257
;--- Espacio para la rutina HOOK
HOOK_CODE: equ DEF_ACCOUNT+257
;Donde colocaremos el gancho antiguo
;(justo despues del codigo nuevo)
HOOK_OLD: equ HOOK_CODE0_END-HOOK_CODE0+HOOK_CODE
ESC_CHAR: equ HOOK_OLD+5