User avatar
lurk101
Posts: 1821
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

Thu Sep 22, 2022 4:10 pm

ejolson wrote:
Thu Sep 22, 2022 1:26 pm
lurk101 wrote:
Sat Sep 17, 2022 2:47 pm
Built and tried it out. The command completion binary executable awareness is really nice! Feels like bash.
Here is some work to create executables in Linux from assembler for Pshell:

viewtopic.php?p=2039701#p2039701

Since there is already an assembler in the Pshell repository, could it be used? I wonder if adding in-line assembly to the built-in C compiler is difficult.
There is currently a disassembler in pshell, but no assembler. There's about 30K of flash left for additional code, so an assembler would fit. I wonder how useful that might be? Since there's no linker, only stand alone programs written in assembler would be possible. The term 'relocatable', as in relocation list, has been used loosely. The code segments are indeed self relocating, but the initialized global data and bss are not. They are compiled to fixed addresses.

Such a program could implement whatever internal calling conventions it wants, but would need to adhere to pshell conventions when calling external SDK or clib functions. This is necessary for maintaining overall pshell integrity. Presently these external entry points are only known by the compiler and loader. A pshell assembler would also need access to those. A cross assembler couldn't know these addresses.
What do people think about a chmod command to set the executable flag?
For the purpose to tagging externally compiled code? hmm, I don't think so.

About the relocation list, it works like this: Since there is only a simple loader, user calls to external (SDK or clib) functions need to be handled in a different way. These functions exist in pshell's code and their address can change from one pshell compilation to the next. We don't want to have to recompile every pshell C program with each new version of pshell. So what do we do?

For compile and go runs we do nothing. The compiler generates the required call instruction to the appropriate external function.

For compile to executable, we do something different. Instead of generating a call, the compiler stores the external function table index of the function directly in the code, then adds its offset from start of code to the relocation list. The loader, after copying the code segment to memory can then process this list and replace these stored indexes with the appropriate call instruction from the current external function table. There are currently over 200 external functions currently defined in the table.

pshell's calling conventions and stack frame are simpler and different from eabi conventions, which leads to complications interfacing with external functions. It's a murky subject, specially when it comes to printf and sprintf... maybe for a future post.

ejolson
Posts: 9979
Joined: Tue Mar 18, 2014 11:47 am

Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

Thu Sep 22, 2022 4:37 pm

lurk101 wrote:
Thu Sep 22, 2022 4:10 pm
ejolson wrote:
Thu Sep 22, 2022 1:26 pm
lurk101 wrote:
Sat Sep 17, 2022 2:47 pm
Built and tried it out. The command completion binary executable awareness is really nice! Feels like bash.
Here is some work to create executables in Linux from assembler for Pshell:

viewtopic.php?p=2039701#p2039701

Since there is already an assembler in the Pshell repository, could it be used? I wonder if adding in-line assembly to the built-in C compiler is difficult.
There is currently a disassembler in pshell, but no assembler. There's about 30K of flash left for additional code, so an assembler would fit. I wonder how useful that might be? Since there's no linker, only stand alone programs written in assembler would be possible.
Thanks for clarifying. I saw the disassembler and somehow thought there was already an assembler as well.

According to Fido an inline assembler similar to how it's done in gcc that allows code like

Code: Select all

#include <stdio.h>

int main(){
	printf("Inline assembler demonstration:\n");
	asm("...M0+ assembly code here...");
	printf("done.\n");
	return 0;
}
could easily fill up 30K of flash.

Alternatively, one could follow the style of Turbo Pascal with a syntax like

Code: Select all

	inline(hex codes and symbols);
that avoids the assembler in analogy to
    inline.png
    inline.png (103.37 KiB) Viewed 773 times
    http://nonagon.org/ExLibris/sites/defau ... sembly.pdf

    BBC Basic on the Pico actually has an inline assembler.

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Thu Sep 22, 2022 6:05 pm

    An inline assembler would certainly be better than an external one. But then I'd question its value. This is not an interpreted or emulated language where you can get large performance improvements with assembly code.

    User avatar
    jahboater
    Posts: 8263
    Joined: Wed Feb 04, 2015 6:38 pm
    Location: Wonderful West Dorset

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Thu Sep 22, 2022 6:13 pm

    ejolson wrote:
    Thu Sep 22, 2022 4:37 pm
    According to Fido an inline assembler similar to how it's done in gcc that allows code like
    There are three (at least) common ways of dealing with inline assembler.

    One was is for the compiler to save all the registers (and any other state) and then blindly execute the users assembler instructions (which were assembled by a separate assembler). The machine state is restored afterwards. Yuk.

    Another method is for the compiler to understand the assembler and do the assembly itself. Then the compiler knows what has been modified by the user assembler instructions and can deal with it (by saving only what is needed perhaps). Harder.

    The GCC way is for the compiler to know the important details of the user assembler code - what gets changed, what doesn't get changed, what is corrupted, and so on. But the assembly is not done by the compiler. This is by far the best way, but it is complex to implement and a steep learning curve for the user.
    In GCC there is no overhead at all for inline assembler and the inline assembler statement is properly optimized within the surrounding code.

    The GCC way is the only practical way I think and the other tier 1 compilers copy GCC's inline asm syntax for that reason.

    Note that GCC outputs assembler code anyway, so the users inline assembler is simply inserted at the right place.

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Thu Sep 22, 2022 6:41 pm

    jahboater wrote:
    Thu Sep 22, 2022 6:13 pm
    ejolson wrote:
    Thu Sep 22, 2022 4:37 pm
    According to Fido an inline assembler similar to how it's done in gcc that allows code like
    There are three (at least) common ways of dealing with inline assembler.

    One was is for the compiler to save all the registers (and any other state) and then blindly execute the users assembler instructions (which were assembled by a separate assembler). The machine state is restored afterwards. Yuk.

    Another method is for the compiler to understand the assembler and do the assembly itself. Then the compiler knows what has been modified by the user assembler instructions and can deal with it (by saving only what is needed perhaps). Harder.

    The GCC way is for the compiler to know the important details of the user assembler code - what gets changed, what doesn't get changed, what is corrupted, and so on. But the assembly is not done by the compiler. This is by far the best way, but it is complex to implement and a steep learning curve for the user.
    In GCC there is no overhead at all for inline assembler and the inline assembler statement is properly optimized within the surrounding code.

    The GCC way is the only practical way I think and the other tier 1 compilers copy GCC's inline asm syntax for that reason.

    Note that GCC outputs assembler code anyway, so the users inline assembler is simply inserted at the right place.
    My only beef with GCC's way is there's no way to tell it not to touch certain registers. You can flag registers as clobbered but that doesn't mean don't touch. In trying to understand my intent GCC has often screwed up. It's the main reason there's a .S file in this project.

    User avatar
    jahboater
    Posts: 8263
    Joined: Wed Feb 04, 2015 6:38 pm
    Location: Wonderful West Dorset

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Thu Sep 22, 2022 8:25 pm

    lurk101 wrote:
    Thu Sep 22, 2022 6:41 pm
    My only beef with GCC's way is there's no way to tell it not to touch certain registers.
    When you say "it" what do you mean? The compiler or the inline assembler code?
    I think you can declare that a variable in C must go in a particular register - and so that register will never be touched (apart from anything you do with that variable). Try something like this to ensure the compiler will not touch r5 (say):

    Code: Select all

    register int dummy asm("r5") = 0;
    As for the inline assembler code, you have complete control over what registers are touched - its nothing to do with the compiler.
    lurk101 wrote:
    Thu Sep 22, 2022 6:41 pm
    You can flag registers as clobbered but that doesn't mean don't touch.
    "Clobbered" simply tells the compiler that the inline assembler code alters the value in a register (or memory or flags) - so the compiler cannot maintain C variables in it past that point.

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 12:06 am

    jahboater wrote:
    Thu Sep 22, 2022 8:25 pm
    lurk101 wrote:
    Thu Sep 22, 2022 6:41 pm
    My only beef with GCC's way is there's no way to tell it not to touch certain registers.
    When you say "it" what do you mean? The compiler or the inline assembler code?
    I think you can declare that a variable in C must go in a particular register - and so that register will never be touched (apart from anything you do with that variable). Try something like this to ensure the compiler will not touch r5 (say):

    Code: Select all

    register int dummy asm("r5") = 0;
    As for the inline assembler code, you have complete control over what registers are touched - its nothing to do with the compiler.
    lurk101 wrote:
    Thu Sep 22, 2022 6:41 pm
    You can flag registers as clobbered but that doesn't mean don't touch.
    "Clobbered" simply tells the compiler that the inline assembler code alters the value in a register (or memory or flags) - so the compiler cannot maintain C variables in it past that point.
    I might be missing something, but this

    Code: Select all

        asm volatile("mov  %1, sp \n"
                     "mov  r0, %3 \n"
                     "push {r0}   \n"
                     "mov  r0, %4 \n"
                     "push {r0}   \n"
                     "blx  %2     \n"
                     "add  sp, #8 \n"
                     "mov  %0, r0 \n"
                     : "=r"(rslt), "=r"(exit_sp)
                     : "r"(exe.entry | 1), "r"(argc), "r"(argv)
                     : "r0");
    
    generates this broken code

    Code: Select all

    => 0x1000ca14 <cc+2164>:        ldr     r2, [sp, #32]
       0x1000ca16 <cc+2166>:        movs    r3, #1
       0x1000ca18 <cc+2168>:        orrs    r2, r3
       0x1000ca1a <cc+2170>:        ldr     r3, [sp, #8]
       0x1000ca1c <cc+2172>:        ldr     r4, [sp, #12]
       0x1000ca1e <cc+2174>:        mov     r3, sp
       0x1000ca20 <cc+2176>:        adds    r0, r3, #0
       0x1000ca22 <cc+2178>:        push    {r0}
       0x1000ca24 <cc+2180>:        adds    r0, r4, #0
       0x1000ca26 <cc+2182>:        push    {r0}
       0x1000ca28 <cc+2184>:        blx     r2
       0x1000ca2a <cc+2186>:        add     sp, #8
       0x1000ca2c <cc+2188>:        adds    r2, r0, #0
       0x1000ca2e <cc+2190>:        str     r2, [sp, #20]
    

    ejolson
    Posts: 9979
    Joined: Tue Mar 18, 2014 11:47 am

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 2:17 am

    lurk101 wrote:
    Mon Sep 19, 2022 8:23 pm
    Should be fixed in master branch now.
    This is to confirm that the problem reported in

    viewtopic.php?p=2039180#p2039180

    is now fixed. Thanks for that!

    ejolson
    Posts: 9979
    Joined: Tue Mar 18, 2014 11:47 am

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 2:24 am

    fdufnews wrote:
    Wed Sep 21, 2022 3:54 pm
    My first try at checking the guessed words was badly coded and very slow.
    Here is a new code that check if the guess is part of the words in the barkle.txt file.
    Can probably be optimized but the check is fast.
    Some parts of the code assume that in barkle.txt lines end with a linefeed (0x0A) and no carriage return (0x0D)
    Everywhere in the code it is assumed that we are playing with five letter words.
    No correction when entering your guess, no support for backspace. Just put garbage and the input is rejected not accounted as a guess

    The dictionnary was generated under linux with this lines in a terminal

    Code: Select all

    > LC_COLLATE=ASCII
    > export LC_COLLATE
    > grep -E -e ^[[:lower:]]{5}$ /usr/share/dict/british-english > barkle.txt
    
    barkle.c

    Code: Select all

    /* Barkle 
    play with 5 letter words
    the words are in a dictionnary that's named barkle.txt
    The dictionnary shall be in the same directory as the code
    fdufnews 2022-09-21
    */
    
    #define NBLETTERS 5
    #define NBTRIES 6
    int file = 0;
    char wordfile[20]="barkle";
    
    
    int countWord(void){
    char filename[20]="";
    
      strcpy(filename, wordfile);
      strcat(filename, ".txt");
      
      int file = open(filename, O_RDONLY);
      int count=-1;
    
      if (file < 0){
        printf("Can't open word list\n");
        goto exit;
      }
      char car;
      while(read(file, &car, 1) >0){
        if (car == 10) count++;
      }
      close(file);
    
      exit:;
      return count;
    }
    
    int checkWord(char buffer[NBLETTERS + 1]){
    char filename[20]="";
    int index;
    int fword;
    int ret = 0;
    char car;
    char letter;
    int end;
    int cmp;
    char word[NBLETTERS + 1];
    
      strcpy(filename, wordfile);
      strcat(filename, ".txt");
      
      fword = open(filename, O_RDONLY);
      
      if(fword == 0){
        printf("can't open file");
        goto return_close;
      }
      index = 0;
      letter = buffer[0];
      lseek(fword, index, 0);
      while(read(file, &car, 1) > 0){
        if(car == letter) break;
        index += NBLETTERS + 1;
        lseek(fword, index, 0);
      }
      if(car == letter){
        end = 0;
        word[NBLETTERS] = 0;
        while( end == 0){
          lseek(fword, index, 0);
          end = (read(fword, word,NBLETTERS) != NBLETTERS) || (letter != word[0]);
          if(strcmp(buffer, word) == 0){
            ret = 1;
            break;
          }
          index += NBLETTERS + 1;
        }
      }
    
    return_close:;  
      close(fword);
      return ret;
    }
    
    void rules(void){
    
      printf("\nBARKLE\n");
      printf("Guess a word\n");
      printf("The Pico chooses a random word of %d letters\n", NBLETTERS);
      printf("Enter your guess and the Pico will return the comparison\nbetween the word and your guess using the following coding\n");
      printf(" . no match\n");
      printf(" - your guess matches a letter but it is not at the right place\n");
      printf(" + your guess matches a letter and it is at the right place\n\n");
      printf(" You are limited to %d guesses\n\n", NBTRIES);
    }
    
    int getWord(char buffer[NBLETTERS + 1], int num){
    char filename[20]="";
    
      strcpy(filename, wordfile);
      strcat(filename, ".txt");
    
      int ret = -1;
      file = open(filename, O_RDONLY);
      buffer[0] = 0;
      if (file < 0){
        printf("Can't open word list\n");
        return ret;
      }
      int count=num;
      char car;
      while(count){
        if (read(file, &car, 1) < 0){
          close(file);
          return ret;
        }
        if (car == 10) count--;
      }
      ret = count;
      read(file, buffer, NBLETTERS);
      buffer[NBLETTERS] = 0;
      close(file);
      return ret;
    }
    
    void readln(char buffer[NBLETTERS + 1], int n){
    
      int ret = -1;
      buffer[0] = 0;
      int ok = 0, count = 0;
      char car;
      
      while(!ok){
        car = getchar();
        if (car < 'a' || car > 'z'){
          if (car == 3) exit(1);
          putchar(7);
        } else {
          buffer[count++] = car;
          buffer[count] = 0;
          putchar(car);
        }
        ok = (count == n);
      }
    }
    
    int compare(char buffer[NBLETTERS + 1], char input[NBLETTERS + 1], char result[NBLETTERS + 1]){
    
    int ret = 0;
    int i, j;
      for( i = 0; i < NBLETTERS; i++){
        result[i] = (char)'.';
      }
      result[NBLETTERS]=0;
      for( i = 0; i < NBLETTERS; i++){
        if (input[i] == buffer[i]){
          ret++;
          result[i] = (char)'+';
        }
      }
      for( i = 0; i < NBLETTERS; i++){
        if (result[i] != '+'){
            for( j = 0; j < NBLETTERS; j++){
              if(input[j] == buffer[i] && result[j] != '+' && result[j] != '-'){
                result[j] = '-';
                break;
              }
            }
        }
      }
      return ret;
    }
    
    int main(int ac, char* av[]){
    
    char buffer[NBLETTERS + 1];
    char input[NBLETTERS + 1];
    char result[NBLETTERS + 1];
    int num = 0;
    int nbWord = 0;
    int nbok = 0;
    int ret = -1;
    int tries = 0;
    int end = 0;
    
      rules();
      srand(time_us_32());
      nbWord = countWord();
      if(nbWord == -1){
        goto force_exit;
      }
      printf("Playing with %d words\n", nbWord);
        
      while(!end){
        num = rand() % nbWord;
        getWord(buffer, num);
        nbok = 0;
        tries = 0;
        while(nbok < NBLETTERS && tries < NBTRIES){
          printf("\n\ninput your guess");
          printf("\x1B[1A");
          do{
            printf("\x0D\x1B[2K");
            readln(input, NBLETTERS);
          }while(checkWord(input) == 0);
          nbok = compare(buffer, input, result);
          printf("\n\x1B[2K");
          printf("%s", result);
          tries++;
        }
        if(tries <= NBTRIES && nbok == NBLETTERS){
          printf("\nYou won\n\n");
        } else {
          printf("\nYou loose, the word was %s\n",buffer);
        }
        printf("Continue (y/n)?:");
        end = getchar() == 'n';
        
      }
      
      ret = 0;
      
    force_exit:;
      return ret;
    }
    EDIT: some changes to the code in order to help modification of word length
    Woohoo! Here is a winning run:

    Code: Select all

    /barkle/: cc fdufnews.c 
    
    
    BARKLE
    Guess a word
    The Pico chooses a random word of 5 letters
    Enter your guess and the Pico will return the comparison
    between the word and your guess using the following coding
     . no match
     - your guess matches a letter but it is not at the right place
     + your guess matches a letter and it is at the right place
    
     You are limited to 6 guesses
    
    Playing with 4593 words
    
    hippo
    .....
    bunny
    +....
    bests
    +-...
    bread
    ++++.
    break
    +++++
    You won
    
    Continue (y/n)?:
    
    Everything ran fast and there were no perceptible delays in verifying the words or scoring them. What happens if your guess has a repeated letter but the mystery word has only one occurrence of that letter?

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 2:50 am

    Doesn't the original use color encoding of the guessed word's actual letters instead of + - and dot.
    Ascii color setting escape sequences.

    Code: Select all

    \e[0;30m 	Black
    \e[0;31m 	Red
    \e[0;32m 	Green
    \e[0;33m 	Yellow
    \e[0;34m 	Blue
    \e[0;35m 	Purple
    \e[0;36m 	Cyan
    \e[0;37m 	White

    ejolson
    Posts: 9979
    Joined: Tue Mar 18, 2014 11:47 am

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 3:30 am

    lurk101 wrote:
    Fri Sep 23, 2022 2:50 am
    Doesn't the original use color encoding of the guessed word's actual letters instead of + - and dot.
    Ascii color setting escape sequences.

    Code: Select all

    \e[0;30m 	Black
    \e[0;31m 	Red
    \e[0;32m 	Green
    \e[0;33m 	Yellow
    \e[0;34m 	Blue
    \e[0;35m 	Purple
    \e[0;36m 	Cyan
    \e[0;37m 	White
    I think the original was written in 1973 by Charles Reid.

    http://www.bitsavers.org/pdf/dec/_Books ... f#page=236

    According to the dog developer, the main innovation with the version Josh Wardle wrote (other than color) is that it can be played only once a day.

    Is there a way to limit play of barkle to once a day? Could the Pico clock when the program last ran be stored someplace and then compared to the present clock when someone tries to run the program again? Maybe Pshell could have a /tmp directory the contents of which is erased at each reboot.

    fdufnews
    Posts: 388
    Joined: Fri Oct 07, 2011 5:37 pm

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 7:20 am

    What happens if your guess has a repeated letter but the mystery word has only one occurrence of that letter?
    Only one occurrence of the letter is reported

    Code: Select all

    BARKLE
    Guess a word
    The Pico chooses a random word of 5 letters
    Enter your guess and the Pico will return the comparison
    between the word and your guess using the following coding
     . no match
     - your guess matches a letter but it is not at the right place
     + your guess matches a letter and it is at the right place
    
     You are limited to 6 guesses
    
    Playing with 4636 words
    
    elves
    +.--.
    
    

    User avatar
    jahboater
    Posts: 8263
    Joined: Wed Feb 04, 2015 6:38 pm
    Location: Wonderful West Dorset

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 8:29 am

    lurk101 wrote:
    Fri Sep 23, 2022 12:06 am
    I might be missing something, but this

    Code: Select all

        asm volatile("mov  %1, sp \n"
                     "mov  r0, %3 \n"
                     "push {r0}   \n"
                     "mov  r0, %4 \n"
                     "push {r0}   \n"
                     "blx  %2     \n"
                     "add  sp, #8 \n"
                     "mov  %0, r0 \n"
                     : "=r"(rslt), "=r"(exit_sp)
                     : "r"(exe.entry | 1), "r"(argc), "r"(argv)
                     : "r0");
    
    generates this broken code
    The compiler does not understand the inline assembler and most definitely does not change the sequence of instructions.
    To change them would be missing the whole point of inline assembler.

    My guess is you are disassembling the wrong code or some other problem.

    Please could you try something like this:

    Code: Select all

    gcc -Os -S -fverbose-asm hello.c -o hello.s
    In the output in the .s file the inlined code is shown like this in the #APP sections:

    Code: Select all

    // x.c:1857:   asm("svc 0" : "+r" (w0) : "r" (x1), "r" (x2), "r" (w8) : "memory");
    #APP
    // 1857 "x.c" 1
            svc 0
    // 0 "" 2
    // x.c:1858:   if( w0 )
    #NO_APP
            cbnz    w0, .L1 // <retval>,
    
    If you then find that GCC is changing your assembler then you should raise a sev 1 bug at:
    https://gcc.gnu.org/
    as it is a complete disaster :(

    fdufnews
    Posts: 388
    Joined: Fri Oct 07, 2011 5:37 pm

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 8:50 am

    lurk101 wrote:
    Fri Sep 23, 2022 2:50 am
    Doesn't the original use color encoding of the guessed word's actual letters instead of + - and dot.
    And now with color

    Code: Select all

    /* Barkle 
    play with 5 letter words
    the words are in a dictionnary that's named barkle.txt
    The dictionnary shall be in the same directory as the code
    fdufnews 2022-09-23
    */
    
    #define NBLETTERS 5
    #define NBTRIES 6
    #define BLACK_ON_GREEN "\x1B[0;30;42m"
    #define BLACK_ON_YELLOW"\x1B[0;30;43m"
    #define GRAY_ON_BLACK "\x1B[2;37;40m"
    #define RESET_ATTR "\x1B[0m"
    
    int file = 0;
    char wordfile[20]="barkle";
    
    
    void rules(void){
    
      printf("\nBARKLE\n");
      printf("Guess a word\n");
      printf("The Pico chooses a random word of %d letters\n", NBLETTERS);
      printf("Enter your guess and the Pico will return the comparison\nbetween the word and your guess using the following coding\n");
      printf(" ");printf(GRAY_ON_BLACK); printf("a"); printf(RESET_ATTR);printf(" ");
      printf("no match\n");
      printf(" ");printf(BLACK_ON_YELLOW); printf("a"); printf(RESET_ATTR);printf(" ");
      printf("your guess matches a letter but it is not at the right place\n");
      printf(" ");printf(BLACK_ON_GREEN); printf("a"); printf(RESET_ATTR);printf(" ");
      printf("your guess matches a letter and it is at the right place\n\n");
      printf(" You are limited to %d guesses\n\n", NBTRIES);
    }
    
    int countWord(void){
    char filename[20]="";
    
      strcpy(filename, wordfile);
      strcat(filename, ".txt");
      
      int file = open(filename, O_RDONLY);
      int count=-1;
    
      if (file < 0){
        printf("Can't open word list\n");
        goto exit;
      }
      char car;
      while(read(file, &car, 1) >0){
        if (car == 10) count++;
      }
      close(file);
    
      exit:;
      return count;
    }
    
    int checkWord(char buffer[NBLETTERS + 1]){
    char filename[20]="";
    int index;
    int fword;
    int ret = 0;
    char car;
    char letter;
    int end;
    int cmp;
    char word[NBLETTERS + 1];
    
      strcpy(filename, wordfile);
      strcat(filename, ".txt");
      
      fword = open(filename, O_RDONLY);
      
      if(fword == 0){
        printf("can't open file");
        goto return_close;
      }
      index = 0;
      letter = buffer[0];
      lseek(fword, index, 0);
      while(read(file, &car, 1) > 0){
        if(car == letter) break;
        index += NBLETTERS + 1;
        lseek(fword, index, 0);
      }
      if(car == letter){
        end = 0;
        word[NBLETTERS] = 0;
        while( end == 0){
          lseek(fword, index, 0);
          end = (read(fword, word,NBLETTERS) != NBLETTERS) || (letter != word[0]);
          if(strcmp(buffer, word) == 0){
            ret = 1;
            break;
          }
          index += NBLETTERS + 1;
        }
      }
    
    return_close:;  
      close(fword);
      return ret;
    }
    
    int getWord(char buffer[NBLETTERS + 1], int num){
    char filename[20]="";
    
      strcpy(filename, wordfile);
      strcat(filename, ".txt");
    
      int ret = -1;
      file = open(filename, O_RDONLY);
      buffer[0] = 0;
      if (file < 0){
        printf("Can't open word list\n");
        return ret;
      }
      int count=num;
      char car;
      while(count){
        if (read(file, &car, 1) < 0){
          close(file);
          return ret;
        }
        if (car == 10) count--;
      }
      ret = count;
      read(file, buffer, NBLETTERS);
      buffer[NBLETTERS] = 0;
      close(file);
      return ret;
    }
    
    void readln(char buffer[NBLETTERS + 1], int n){
    
      int ret = -1;
      buffer[0] = 0;
      int ok = 0, count = 0;
      char car;
      
      while(!ok){
        car = getchar();
        if (car < 'a' || car > 'z'){
          if (car == 3) exit(1);
          putchar(7);
        } else {
          buffer[count++] = car;
          buffer[count] = 0;
          putchar(car);
        }
        ok = (count == n);
      }
    }
    
    int compare(char buffer[NBLETTERS + 1], char input[NBLETTERS + 1], char result[NBLETTERS + 1]){
    
    int ret = 0;
    int i, j;
      for( i = 0; i < NBLETTERS; i++){
        result[i] = (char)'.';
      }
      result[NBLETTERS]=0;
      for( i = 0; i < NBLETTERS; i++){
        if (input[i] == buffer[i]){
          ret++;
          result[i] = (char)'+';
        }
      }
      for( i = 0; i < NBLETTERS; i++){
        if (result[i] != '+'){
            for( j = 0; j < NBLETTERS; j++){
              if(input[j] == buffer[i] && result[j] != '+' && result[j] != '-'){
                result[j] = '-';
                break;
              }
            }
        }
      }
      return ret;
    }
    
    #define BLACK_ON_GREEN "\x1B[0;30;42m"
    #define BLACK_ON_YELLOW"\x1B[0;30;43m"
    #define GRAY_ON_BLACK "\x1B[2;37;40m"
    #define RESET_ATTR "\x1B[0m"
    
    void printResult(char input[NBLETTERS + 1], char result[NBLETTERS + 1]){
    int i;
      for(i = 0; i < NBLETTERS; i++){
        switch(result[i]){
          case '.':
            printf(GRAY_ON_BLACK);
            break;
          case '-':
            printf(BLACK_ON_YELLOW);
            break;
          case '+':
            printf(BLACK_ON_GREEN);
            break;
        }
        printf("%c", input[i]);
      }
      printf(RESET_ATTR);
    }
    
    int main(int ac, char* av[]){
    
    char buffer[NBLETTERS + 1];
    char input[NBLETTERS + 1];
    char result[NBLETTERS + 1];
    int num = 0;
    int nbWord = 0;
    int nbok = 0;
    int ret = -1;
    int tries = 0;
    int end = 0;
    
      rules();
      srand(time_us_32());
      nbWord = countWord();
      if(nbWord == -1){
        goto force_exit;
      }
      printf("Playing with %d words\n", nbWord);
        
      while(!end){
        num = rand() % nbWord;
        getWord(buffer, num);
        nbok = 0;
        tries = 0;
        while(nbok < NBLETTERS && tries < NBTRIES){
          printf("\n\ninput your guess");
          printf("\x1B[1A");
          do{
            printf("\x0D\x1B[2K");
            readln(input, NBLETTERS);
          }while(checkWord(input) == 0);
          nbok = compare(buffer, input, result);
          printf("\r\x1B[2K");
          printResult(input, result);
          tries++;
        }
        printf("\n\x1B[2K");
        if(tries <= NBTRIES && nbok == NBLETTERS){
          printf("You won\n\n");
        } else {
          printf("You loose, the word was %s\n\n",buffer);
        }
        printf("Continue (y/n)?:");
        end = getchar() == 'n';
        printf("\n");
      }
      
      ret = 0;
      
    force_exit:;
      return ret;
    }

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 11:46 am

    jahboater wrote:
    Fri Sep 23, 2022 8:29 am
    lurk101 wrote:
    Fri Sep 23, 2022 12:06 am
    I might be missing something, but this

    Code: Select all

        asm volatile("mov  %1, sp \n"
                     "mov  r0, %3 \n"
                     "push {r0}   \n"
                     "mov  r0, %4 \n"
                     "push {r0}   \n"
                     "blx  %2     \n"
                     "add  sp, #8 \n"
                     "mov  %0, r0 \n"
                     : "=r"(rslt), "=r"(exit_sp)
                     : "r"(exe.entry | 1), "r"(argc), "r"(argv)
                     : "r0");
    
    generates this broken code
    The compiler does not understand the inline assembler and most definitely does not change the sequence of instructions.
    I believe you are wrong. Above, for example, the compiler must add instructions to calculate the value of of 'exe.entry | 1'. Outputs must be lvalues, but Inputs can be expressions.
    You can see it loading the values of argc and argv to r3 and r4 in the generated code, then immediately clobbers r3 before using it.
    To change them would be missing the whole point of inline assembler.

    My guess is you are disassembling the wrong code or some other problem.

    Please could you try something like this:

    Code: Select all

    gcc -Os -S -fverbose-asm hello.c -o hello.s
    In the output in the .s file the inlined code is shown like this in the #APP sections:

    Code: Select all

    // x.c:1857:   asm("svc 0" : "+r" (w0) : "r" (x1), "r" (x2), "r" (w8) : "memory");
    #APP
    // 1857 "x.c" 1
            svc 0
    // 0 "" 2
    // x.c:1858:   if( w0 )
    #NO_APP
            cbnz    w0, .L1 // <retval>,
    
    If you then find that GCC is changing your assembler then you should raise a sev 1 bug at:
    https://gcc.gnu.org/
    as it is a complete disaster :(
    Nope, the right code was disassembled. This coerced gcc to do the right thing.

    Code: Select all

        int ep = exe.entry | 1;
        asm volatile("mov  %0, sp \n" : "=r"(exit_sp));
        asm volatile("mov  r0, %0 \n"
                     "push {r0}   \n"
                     "mov  r0, %1 \n"
                     "push {r0}   \n"
                     :
                     : "r"(argc), "r"(argv));
        asm volatile("blx  %1     \n"
                     "add  sp, #8 \n"
                     "mov  %0, r0 \n"
                     : "=r"(rslt)
                     : "r"(ep));
                     
    I'm using gcc 12.1.

    VincentARM
    Posts: 41
    Joined: Sat Feb 06, 2021 8:00 pm

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 1:52 pm

    An inline assembler would certainly be better than an external one. But then I'd question its value.
    An inline assembler will not have all the possibilities of the standard arm as assembler.
    The external solution with as allows using the same syntax, macros etc.
    Transferring the result from a PC to the pico takes 10 seconds and is done in 3 clicks.

    Thank for the relocation explication. I will look at this in detail and modify the asmpshell.c program to incorporate this particularity.

    I will also look at where the function address table is.

    arg001
    Posts: 54
    Joined: Tue Jan 23, 2018 10:06 am

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 1:54 pm

    lurk101 wrote:
    Fri Sep 23, 2022 11:46 am
    Above, for example, the compiler must add instructions to calculate the value of of 'exe.entry | 1'. Outputs must be lvalues, but Inputs can be expressions.
    You can see it loading the values of argc and argv to r3 and r4 in the generated code, then immediately clobbers r3 before using it.
    I haven't analysed your example in detail, but are you suffering from the issue of overlapping input and output register assignments?

    If so, you need to tag your output operands with "=&r" rather than "=r".

    This is notorious for creating bugs where the code initially works (because the registers happen to be distinct) but then something arbitrary like a change of compiler version or changes miles away in the code suddenly cause it to break. It then looks like "it's the compiler's fault", when in fact the source wasn't adequately constrained and it was only working by fluke.

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 3:47 pm

    arg001 wrote:
    Fri Sep 23, 2022 1:54 pm
    lurk101 wrote:
    Fri Sep 23, 2022 11:46 am
    Above, for example, the compiler must add instructions to calculate the value of of 'exe.entry | 1'. Outputs must be lvalues, but Inputs can be expressions.
    You can see it loading the values of argc and argv to r3 and r4 in the generated code, then immediately clobbers r3 before using it.
    I haven't analysed your example in detail, but are you suffering from the issue of overlapping input and output register assignments?

    If so, you need to tag your output operands with "=&r" rather than "=r".

    This is notorious for creating bugs where the code initially works (because the registers happen to be distinct) but then something arbitrary like a change of compiler version or changes miles away in the code suddenly cause it to break. It then looks like "it's the compiler's fault", when in fact the source wasn't adequately constrained and it was only working by fluke.
    Good to know. I was unaware of the & modifier. In this case I don't think that's the problem. All of the input and output registers are chosen by the compiler for variables that do not overlap. The only hard coded register is R0.

    ejolson
    Posts: 9979
    Joined: Tue Mar 18, 2014 11:47 am

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 3:53 pm

    fdufnews wrote:
    Fri Sep 23, 2022 7:20 am
    What happens if your guess has a repeated letter but the mystery word has only one occurrence of that letter?
    Only one occurrence of the letter is reported

    Code: Select all

    BARKLE
    Guess a word
    The Pico chooses a random word of 5 letters
    Enter your guess and the Pico will return the comparison
    between the word and your guess using the following coding
     . no match
     - your guess matches a letter but it is not at the right place
     + your guess matches a letter and it is at the right place
    
     You are limited to 6 guesses
    
    Playing with 4636 words
    
    elves
    +.--.
    
    
    In the above case is it reasonable to conclude that the mystery word has two e's in it?

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 3:54 pm

    VincentARM wrote:
    Fri Sep 23, 2022 1:52 pm
    An inline assembler would certainly be better than an external one. But then I'd question its value.
    An inline assembler will not have all the possibilities of the standard arm as assembler.
    The external solution with as allows using the same syntax, macros etc.
    Transferring the result from a PC to the pico takes 10 seconds and is done in 3 clicks.

    Thank for the relocation explication. I will look at this in detail and modify the asmpshell.c program to incorporate this particularity.

    I will also look at where the function address table is.
    The external function table is stable in terms of which functions it contains, but the function addresses would likely change with each new compile of pshell.

    I'm also a little puzzled why you'd want to run a host compiled assembler program in pshell. Why not just use the standard Pico development tools? They're hosted, provide better functionality and debugging, and do away with pshell constraints and overhead.

    Inline asm is not something this compiler could handle, and hosted solution issues have been identified. The only viable approach I can see for pshell is a asm command that works within pshell's constraints. Are there any opensource assemblers for ARM thumb source code?

    arg001
    Posts: 54
    Joined: Tue Jan 23, 2018 10:06 am

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 4:20 pm

    lurk101 wrote:
    Fri Sep 23, 2022 3:47 pm
    Good to know. I was unaware of the & modifier. In this case I don't think that's the problem. All of the input and output registers are chosen by the compiler for variables that do not overlap. The only hard coded register is R0.
    No, that's the point: without the "=&r", the compiler is allowed to use the same register for one of the inputs and one of the outputs - it assumes that you consume all of the inputs before starting to write the outputs.

    It looks like your example code violates this -

    Code: Select all

    asm volatile("mov  %1, sp \n"
                     "mov  r0, %3 \n"
                     "push {r0}   \n"
                     "mov  r0, %4 \n"
                     "push {r0}   \n"
                     "blx  %2     \n"
                     "add  sp, #8 \n"
                     "mov  %0, r0 \n"
                     : "=r"(rslt), "=r"(exit_sp)
                     : "r"(exe.entry | 1), "r"(argc), "r"(argv))
    The 1st instruction is using one of the output operands (%1) before capturing the values of the input operands. The compiler is entitled to use the same register for %1 and (for example) %3, in which case the value of %3 gets overwritten.

    In this example, %1 (exit_sp) gets used early and so needs to be "=&r", while %0 is only used at the end so can safely remain "=r".

    Since this is so risky, I tend to use "=&r" by default and only switch to "=r" if I don't like the generated code, though admittedly it's worth it in the case of this example for %0 where a gratuitous "=&r" would require the compiler to free up an extra register and so add extra code.

    I'm also not sure why you are using r0 without declaring it clobbered or otherwise protecting it, though there may be a good reason you are doing that which I can't see out of context.

    fdufnews
    Posts: 388
    Joined: Fri Oct 07, 2011 5:37 pm

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 4:33 pm

    ejolson wrote:
    Fri Sep 23, 2022 3:53 pm
    fdufnews wrote:
    Fri Sep 23, 2022 7:20 am
    What happens if your guess has a repeated letter but the mystery word has only one occurrence of that letter?
    Only one occurrence of the letter is reported

    Code: Select all

    BARKLE
    Guess a word
    The Pico chooses a random word of 5 letters
    Enter your guess and the Pico will return the comparison
    between the word and your guess using the following coding
     . no match
     - your guess matches a letter but it is not at the right place
     + your guess matches a letter and it is at the right place
    
     You are limited to 6 guesses
    
    Playing with 4636 words
    
    elves
    +.--.
    
    
    In the above case is it reasonable to conclude that the mystery word has two e's in it?
    Certainly

    User avatar
    lurk101
    Posts: 1821
    Joined: Mon Jan 27, 2020 2:35 pm
    Location: Cumming, GA (US)

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 4:47 pm

    arg001 wrote:
    Fri Sep 23, 2022 4:20 pm
    No, that's the point: without the "=&r", the compiler is allowed to use the same register for one of the inputs and one of the outputs - it assumes that you consume all of the inputs before starting to write the outputs.
    Ah, I see. Thanks.

    Indeed, that was the problem:

    Code: Select all

        asm volatile("mov  %1, sp \n"
                     "mov  r0, %3 \n"
                     "push {r0}   \n"
                     "mov  r0, %4 \n"
                     "push {r0}   \n"
                     "blx  %2     \n"
                     "add  sp, #8 \n"
                     "mov  %0, r0 \n"
                     : "=r"(rslt), "=&r"(exit_sp)
                     : "r"(exe.entry | 1), "r"(argc), "r"(argv)
                     : "r0");
    
    generates the correct instruction sequence

    Code: Select all

    => 0x1000ca14 <cc+2164>:        ldr     r1, [sp, #32]
       0x1000ca16 <cc+2166>:        movs    r3, #1
       0x1000ca18 <cc+2168>:        orrs    r1, r3
       0x1000ca1a <cc+2170>:        ldr     r3, [sp, #8]
       0x1000ca1c <cc+2172>:        ldr     r4, [sp, #12]
       0x1000ca1e <cc+2174>:        mov     r2, sp
       0x1000ca20 <cc+2176>:        adds    r0, r3, #0
       0x1000ca22 <cc+2178>:        push    {r0}
       0x1000ca24 <cc+2180>:        adds    r0, r4, #0
       0x1000ca26 <cc+2182>:        push    {r0}
       0x1000ca28 <cc+2184>:        blx     r1
       0x1000ca2a <cc+2186>:        add     sp, #8
       0x1000ca2c <cc+2188>:        adds    r1, r0, #0
       0x1000ca2e <cc+2190>:        str     r1, [sp, #20]
       0x1000ca30 <cc+2192>:        ldr     r3, [pc, #100]  ; (0x1000ca98 <cc+2296>)
       0x1000ca32 <cc+2194>:        str     r2, [r3, #16]
    
    which is nothing like the coded asm statements, but correct in that it achieves exactly what is intended.

    User avatar
    jahboater
    Posts: 8263
    Joined: Wed Feb 04, 2015 6:38 pm
    Location: Wonderful West Dorset

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 6:03 pm

    lurk101 wrote:
    Fri Sep 23, 2022 11:46 am
    I believe you are wrong. Above, for example, the compiler must add instructions to calculate the value of of 'exe.entry | 1'. Outputs must be lvalues, but Inputs can be expressions.
    Of course the compiler must add instructions so that the inputs match the constraints before your coded asm instructions start.
    For example if you specify "r" (variable), it may have to load "variable" from memory to get it in a register with an ldr instruction.

    The compiler may also have to save any registers it is currently using that get clobbered or written to, but again, that happens outside of your sequence and is nothing to do with your code.

    There is considerable scope for the compiler to optimize and do its own thing whilst satisfying the constraints.

    But once that is all done, the coded asm statements should execute exactly as you specify:

    mov mov push mov push blx add mov

    If that sequence is altered by the compiler then the whole point of assembler is lost and I simply don't believe it!

    Again, the compiler does not understand the inline assembler code, other than the constraints you have given, so how could it modify it????
    (actually the heuristics for function inlining make a guess at the size of any inline asm code, but its only an estimate).
    lurk101 wrote:
    Fri Sep 23, 2022 11:46 am
    which is nothing like the coded asm statements, but correct in that it achieves exactly what is intended.
    Its still wrong. Your code might have obscure timing issues, or a strange side effect, or some other effect that you are exploiting.
    The compiler cannot just go and alter "your" code!!!!
    If "-S -fverbose-asm" (between the #APP directives) shows the code has changed, this is a very serious bug and should be reported.
    Perhaps your sequence has been moved?

    Anyway I'm glad & and declaring r0 as clobbered makes the code work.
    Last edited by jahboater on Fri Sep 23, 2022 6:38 pm, edited 4 times in total.

    User avatar
    jahboater
    Posts: 8263
    Joined: Wed Feb 04, 2015 6:38 pm
    Location: Wonderful West Dorset

    Re: A tiny Raspberry Pico shell with flash file system, vi, and c compiler.

    Fri Sep 23, 2022 6:13 pm

    VincentARM wrote:
    Fri Sep 23, 2022 1:52 pm
    An inline assembler will not have all the possibilities of the standard arm as assembler.
    The inline assembler is the standard arm as assembler.
    (For GCC on Linux/UNIX anyway).

    The compiler simply inserts the inline asm statements into the assembler output.
    Later the assembler (as) assembles it all.

    Return to “General”