- Adds new ENOEXEC error code, also used by Apple2 targets.
- Maximum command line length is 40, incl. program name. This is
an XDOS restriction.
- testcode/lib/tinyshell.c has been extended to be able to run
programs.
PNTINV = 171 ;($AB) point invalid
BADDSK = 173 ;($AD) bad disk
INCFMT = 176 ;($B0) DOS 3: incompatible file system
+XNTBIN = 180 ;($B4) XDOS: file not binary
; DCB Device Bus Equates
CIOV = $E456 ;vector to CIO
SIOV = $E459 ;vector to SIO
SETVBV = $E45C ;vector to set VBLANK parameters
+; aliases in order not to have to sprinkle common code with .ifdefs
+CIOV_org = CIOV
+SIOV_org = SIOV
+SETVBV_org = SETVBV
.endif
SYSVBV = $E45F ;vector to process immediate VBLANK
XITVBV = $E462 ;vector to process deferred VBLANK
ESPIPE ; Illegal seek
ERANGE ; Range error
EBADF ; Bad file number
+ ENOEXEC ; Exec format error
EUNKNOWN ; Unknown OS specific error - must be last!
EMAX = EUNKNOWN ; Highest error code
#define ESPIPE 14 /* Illegal seek */
#define ERANGE 15 /* Range error */
#define EBADF 16 /* Bad file number */
-#define EUNKNOWN 17 /* Unknown OS specific error */
+#define ENOEXEC 17 /* Exec format error */
+#define EUNKNOWN 18 /* Unknown OS specific error */
.byte $47, EEXIST ; Duplicate filename
.byte $48, ENOSPC ; Volume full
.byte $49, ENOSPC ; Volume directory full
-; .byte $4A, EUNKNOWN ; Incompatible file format
+ .byte $4A, ENOEXEC ; Incompatible file format
.byte $4B, EINVAL ; Unsupported storage_type
; .byte $4C, EUNKNOWN ; End of file encountered
.byte $4D, ESPIPE ; Position out of range
;
.export __STARTUP__ : absolute = 1 ; Mark as startup
- .export _exit, start
+ .export _exit, start, excexit, SP_save
.import initlib, donelib
.import callmain, zerobss
; Call the module destructors. This is also the exit() entry.
-_exit: jsr donelib ; Run module destructors
+_exit: ldx SP_save
+ txs ; Restore stack pointer
; Restore the system stuff.
- ldx SP_save
- txs ; Restore stack pointer
+excexit:jsr donelib ; Run module destructors; 'excexit' is called from the exec routine
; Restore the left margin.
--- /dev/null
+;
+; Christian Groessler, 12-Jun-2016
+;
+; int __fastcall__ exec (const char* progname, const char* cmdline);
+;
+; supports only XDOS at the moment
+
+ .export _exec
+
+ .import popax
+ .import __dos_type
+ .import findfreeiocb
+ .import incsp2
+ .import __do_oserror
+ .import excexit ; from crt0.s
+ .import SP_save ; from crt0.s
+.ifdef UCASE_FILENAME
+ .importzp tmp3
+ .import ucase_fn
+ .import addysp
+.endif
+
+ .include "zeropage.inc"
+ .include "errno.inc"
+ .include "atari.inc"
+
+CMDLINE_BUFFER = $0100 ; put progname + cmdline as one single string there
+CMDLINE_MAX = 40+3 ; max. length of drive + progname + cmdline
+
+ .code
+
+notsupp:lda #ENOSYS ; "unsupported system call"
+ .byte $2C ; bit opcode, eats the next 2 bytes
+noiocb: lda #EMFILE ; "too many open files"
+ jsr incsp2 ; clean up stack
+seterr: jsr __directerrno
+ lda #$FF
+ tax
+ rts ; return -1
+
+; entry point
+
+_exec:
+ ; save cmdline
+ sta ptr3
+ stx ptr3+1
+
+ ldy __dos_type
+ cpy #XDOS
+ bne notsupp
+
+ jsr findfreeiocb
+ bne noiocb
+
+ stx tmp4 ; remember IOCB index
+
+ ; get program name
+ jsr popax
+
+.ifdef UCASE_FILENAME
+.ifdef DEFAULT_DEVICE
+ ldy #$80
+.else
+ ldy #$00
+.endif
+ sty tmp2 ; set flag for ucase_fn
+ jsr ucase_fn
+ bcc ucok1
+invret: lda #EINVAL ; file name is too long
+ bne seterr
+ucok1:
+.endif ; defined UCASE_FILENAME
+
+; copy program name and arguments to CMDLINE_BUFFER
+
+ sta ptr4 ; ptr4: pointer to program name
+ stx ptr4+1
+ ldy #0
+ ; TODO: check stack ptr and and use min(CMDLINE_MAX,available_stack)
+copyp: lda (ptr4),y
+ beq copypd
+ sta CMDLINE_BUFFER,y
+ iny
+ cpy #CMDLINE_MAX
+ bne copyp
+
+ ; programe name too long
+ beq invret
+
+; file name copied, check for args
+
+copypd: tya ; put Y into X (index into CMDLINE_BUFFER)
+ tax
+ lda ptr3
+ ora ptr3+1 ; do we have arguments?
+ beq copycd ; no
+ ldy #0
+ lda (ptr3),y ; get first byte of cmdline parameter
+ beq copycd ; nothing there...
+ lda #' ' ; add a space btw. progname and cmdline
+ bne copyc1
+
+; copy args
+
+copyc: lda (ptr3),y
+ beq copycd
+ iny
+copyc1: sta CMDLINE_BUFFER,x
+ inx
+ cpx #CMDLINE_MAX
+ bne copyc
+ ; progname + arguments too long
+ beq invret
+
+invexe: jsr close
+ lda #XNTBIN
+ bne setmerr
+
+copycd: lda #ATEOL
+ sta CMDLINE_BUFFER,x
+
+; open the program file, read the first two bytes and compare them to $FF
+
+ ldx tmp4 ; get IOCB index
+ lda ptr4 ; ptr4 points to progname
+ sta ICBAL,x
+ lda ptr4+1
+ sta ICBAH,x
+ lda #OPNIN ; open for input
+ sta ICAX1,x
+ lda #OPEN
+ sta ICCOM,x
+ jsr CIOV
+
+ tya
+
+.ifdef UCASE_FILENAME
+ ldy tmp3 ; get size
+ jsr addysp ; free used space on the stack
+ ; the following 'bpl' depends on 'addysp' restoring A as last command before 'rts'
+.endif ; defined UCASE_FILENAME
+
+ bpl openok
+ pha ; remember error code
+ jsr close ; close the IOCB (required even if open failed)
+ pla ; put error code back into A
+setmerr:jmp __mappederrno ; update errno from OS specific error code in A
+
+openok: lda #>buf
+ sta ICBAH,x ; set buffer address
+ lda #<buf
+ sta ICBAL,x
+ lda #0 ; set buffer length
+ sta ICBLH,x
+ lda #2
+ sta ICBLL,x
+ lda #GETCHR ; iocb command code
+ sta ICCOM,x
+ jsr CIOV ; read it
+ bmi invexe ; read operation failed, return error
+
+ lda ICBLL,x ; # of bytes read
+ cmp #2
+ bne invexe
+ lda #$FF ; check file format (need $FFFF at the beginning)
+ cmp buf
+ bne invexe
+ cmp buf+1
+ bne invexe
+
+ jsr close ; close program file
+
+; program file appears to be available and good
+; here's the point of no return
+
+ lda tmp4 ; get IOCB index
+ pha ; and save it ('excexit' calls destructors and they might destroy tmp4)
+ jsr excexit
+ pla
+ ldx SP_save
+ txs ; reset stack pointer
+ tax ; IOCB index in X
+
+ lda #<CMDLINE_BUFFER
+ sta ICBAL,x ; address
+ lda #>CMDLINE_BUFFER
+ sta ICBAH,x
+ lda #0
+ sta ICBLL,x ; length shouldn't be random, but 0 is ok
+ sta ICBLH,x
+ sta ICAX1,x
+ sta ICAX2,x
+ lda #80 ; XDOS: run DUP command
+ sta ICCOM,x
+ jmp CIOV_org ; no way to display an error message in case of failure, and we will return to DOS
+
+
+; close IOCB, index in X
+.proc close
+ lda #CLOSE
+ sta ICCOM,x
+ jmp CIOV ; close IOCB
+.endproc
+
+ .bss
+
+buf: .res 2
.byte EUNKNOWN ; 177 - haven't found documentation
.byte EUNKNOWN ; 178 - haven't found documentation
.byte EUNKNOWN ; 179 - haven't found documentation
- .byte EUNKNOWN ; 180 - not a binary file
+ .byte ENOEXEC ; 180 - not a binary file
.byte EUNKNOWN ; 181 - [MYDOS] invalid address range
.byte EUNKNOWN ; 182 - [XDOS] invalid parameter
"Illegal seek", /* ESPIPE */
"Range error", /* ERANGE */
"Bad file number", /* EBADF */
+ "Exec format error", /* ENOEXEC */
"Unknown OS error code", /* EUNKNOWN */
};
/*
** Simple ("tiny") shell to test filename and directory functions.
-** Copyright (c) 2013, Christian Groessler, chris@groessler.org
+** Copyright (c) 2013,2016 Christian Groessler, chris@groessler.org
*/
-#define VERSION_ASC "0.90"
+#define VERSION_ASC "0.91"
#ifdef __ATARI__
#define UPPERCASE /* define (e.g. for Atari) to convert filenames etc. to upper case */
#define CHECK_SP
#endif
-#define KEYB_BUFSZ 80
+#define KEYB_BUFSZ 127
#define PROMPT ">>> "
#include <stdio.h>
#define CMD_PWD 11
#define CMD_CLS 12
#define CMD_VERBOSE 13
+#define CMD_EXEC 14
static unsigned char verbose;
static unsigned char terminate;
static unsigned char cmd;
-static unsigned char *cmd_asc, *arg1, *arg2, *arg3;
-static unsigned char keyb_buf[KEYB_BUFSZ];
+static unsigned char *cmd_asc, *arg1, *arg2, *arg3, *args; /* 'args': everything after command */
+static unsigned char keyb_buf[KEYB_BUFSZ + 1];
+static unsigned char keyb_buf2[KEYB_BUFSZ + 1];
static size_t cpbuf_sz = 4096;
struct cmd_table {
{ "mv", CMD_RENAME },
{ "ren", CMD_RENAME },
{ "pwd", CMD_PWD },
+ { "exec", CMD_EXEC },
#ifdef __ATARI__
{ "cls", CMD_CLS },
#endif
return;
}
+ /* put everything after first string into 'args' */
+
+ strcpy(keyb_buf2, keyb_buf); /* use a backup copy for 'args' */
+
+ /* skip over the first non-whitespace item */
+ cmd_asc = strtok(keyb_buf2, " \t\n");
+ if (cmd_asc)
+ args = strtok(NULL, ""); /* get everything */
+ else
+ *args = 0; /* no arguments */
+
/* split input into cmd, arg1, arg2, arg3 */
/* get and parse command */
puts("cd, chdir - change directory or drive");
puts("md, mkdir - make directory or drive");
puts("rd, rmdir - remove directory or drive");
+ puts("exec - run program");
#ifdef __ATARI__
puts("cls - clear screen");
#endif
puts("verbose - set verbosity level");
- puts("sorry, you cannot start programs here");
}
static void cmd_ls(void)
printf("rename failed: %s\n", strerror(errno));
}
+static void cmd_exec(void)
+{
+ int st;
+ unsigned char *progname, *arguments;
+
+ progname = strtok(args, " \t\n");
+ if (! progname) {
+ puts("usage: exec <progname> [arguments]");
+ return;
+ }
+ arguments = strtok(NULL, "");
+
+ /*printf("exec: %s %s\n", progname, arguments ? arguments : "");*/
+ st = exec(progname, arguments);
+ printf("exec error: %s\n", strerror(errno));
+}
+
static void cmd_copy(void)
{
int srcfd = -1, dstfd = -1;
case CMD_RMDIR: cmd_rmdir(); return;
case CMD_PWD: cmd_pwd(); return;
#endif
+ case CMD_EXEC: cmd_exec(); return;
case CMD_RENAME: cmd_rename(); return;
case CMD_COPY: cmd_copy(); return;
#ifdef __ATARI__