Thread: Converting MINI-BASIC in MASM over to C++?

  1. #91
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> To make it "binary compatible" with the existing assembly code, I think reading documentation may not be sufficient.

    Good point.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  2. #92
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    PEEK and POKE work within the code itself - we may need to change the way the code is stored to solve that. Let's ignore PEEK and POKE for the very moment.

    Locate is a "gotoxy()" type function to move the cursor.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  3. #93
    Registered User
    Join Date
    Jun 2004
    Posts
    76
    Yup, LOCATE is used to position to cursor anywhere on the screen.

    Paul

  4. #94
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    I had a quick look a the "pitman.mbi", and it looks like it's using PEEK and POKE to store extra data. It's essentially building data structures in memory, and reading them back in other places.

    So we need to make PEEK and POKE available. But we probably just need a section of memory available for the purpose.

    I improved the "LET" command a bit:
    Code:
    void LET(const char *str)
    {
    	int *ptr;
    	if (GetVariableAddress(&str, &ptr))
    	{
    		if (SkipSpaces(&str, '='))
    		{
    			if (!EvalExpr(&str, ptr))
    			{
    				SyntaxError();
    			}
    		}
    	}
    	
    }
    Saves an if-statement and a str++. And gives a "SyntaxError" if the expression is not valid.

    --
    Mats
    Last edited by matsp; 03-19-2009 at 04:14 PM.
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  5. #95
    Registered User
    Join Date
    Jun 2004
    Posts
    76
    Looking good.

    Paul

  6. #96
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Code:
    #define WINAPITEST(RESULT, EXPECTED) mbox_error_handler(__FILE__, __LINE__, ((RESULT) == (EXPECTED)))
    ^_^

    Well, call it whatever you like, but you also need to add a few extra parentheses. (I wasn't thinking.)

    I've patched some 'init' and 'shdn' stuff as well as fixed the resize function--I think.

    I'm actually cobbling this together as I go so it doesn't match what you have.

    Soma

    Code:
    #define WINAPIERRORWITH(BOOLEAN) mbox_error_handler(__FILE__, __LINE__, (BOOLEAN))
    #define BAD_PARAMETER() throw(__FUNCTION__)
    
    bool free_console_g(false);
    
    WORD console_original_attributes_g(0);
    COORD console_original_buffer_size_g;
    SMALL_RECT console_original_window_size_g;
    
    const DWORD original_console_title_size_g(2048);
    char original_WinTitle[original_console_title_size_g];
    
    std::int32_t console_width_g(0);
    std::int32_t console_height_g(0);
    
    void resize_console_buffer
    (
       const std::int32_t width_f,
       const std::int32_t height_f
    );
    
    void resize_console_window
    (
       const std::int32_t width_f,
       const std::int32_t height_f
    );
    
    void clear()
    {
       DWORD written(0);
       COORD origin = { 0, 0 };
       WORD attributes(console_original_attributes_g);
       DWORD count(console_original_buffer_size_g.X * console_original_buffer_size_g.Y);
       WINAPITEST(FillConsoleOutputAttribute(hStdOut, attributes, count, origin, &written), 0);
       WINAPITEST(FillConsoleOutputCharacterA(hStdOut, ' ', count, origin, &written), 0);
       WINAPITEST(SetConsoleCursorPosition(hStdOut, origin), 0);
    }
    
    void resize_console_buffer
    (
       const std::int32_t width_f,
       const std::int32_t height_f
    )
    {
       // where are these constants declared?
       // are they even constant?
       // maybe these are from `GetSystemMetrics'?
       // these values are reasonable for most systems
       if((width_f <= 3000) && (height_f <= 5000))
       {
          if((width_f < console_width_g) || (height_f < console_height_g))
          {
             resize_console_window(width_f, height_f);
          }
          COORD size = {width_f, height_f};
          WINAPITEST(SetConsoleScreenBufferSize(hStdOut, size), 0);
       }
       else
       {
          BAD_PARAMETER();
       }
    }
    
    void resize_console_window
    (
       const std::int32_t width_f,
       const std::int32_t height_f
    )
    {
       CONSOLE_SCREEN_BUFFER_INFO csbi;
       WINAPITEST(GetConsoleScreenBufferInfo(hStdOut, &csbi), 0);
       if(!((width_f <= csbi.dwMaximumWindowSize.X) && (height_f <= csbi.dwMaximumWindowSize.Y)))
       {
          resize_console_buffer(width_f, height_f);
       }
       SMALL_RECT size_by_rectangle = {0, 0, width_f - 1, height_f - 1};
       WINAPITEST(SetConsoleWindowInfo(hStdOut, TRUE, &size_by_rectangle), 0);
       console_width_g = width_f;
       console_height_g = height_f;
    }
    
    void resize_console
    (
       const std::int32_t width_f,
       const std::int32_t height_f
    )
    {
       resize_console_buffer(width_f, height_f);
       resize_console_window(width_f, height_f);
    }
    
    void startup()
    {
       // test if we need to allocate our console window
       if(0 == GetConsoleTitleA(original_WinTitle, original_console_title_size_g))
       {
          if(6 == GetLastError())
          {
             WINAPITEST(AllocConsole(), 0);
             WINAPITEST(GetConsoleTitleA(original_WinTitle, original_console_title_size_g), 0);
             free_console_g = true;
          }
          else
          {
             WINAPIERRORWITH(true);
          }
       }
       WINAPITEST(SetConsoleTitleA(WinTitle), 0);
       {
          CONSOLE_SCREEN_BUFFER_INFO csbi;
          WINAPITEST(GetConsoleScreenBufferInfo(hStdOut, &csbi), 0);
          console_original_attributes_g = csbi.wAttributes;
          console_original_buffer_size_g = csbi.dwSize;
          WINAPITEST(SetConsoleCursorPosition(hStdOut, origin), 0);
          console_original_window_size_g = csbi.srWindow;
          console_width_g = console_original_window_size_g.Right + 1;
          console_height_g = console_original_window_size_g.Bottom + 1;
          resize_console(EditSize.X, EditSize.Y);
          clear();
       }
    }
    
    void shutdown()
    {
       resize_console(console_original_window_size_g.Right + 1, console_original_window_size_g.Bottom + 1);
       resize_console_buffer(console_original_buffer_size_g.X, console_original_buffer_size_g.Y);
       WINAPITEST(SetConsoleTitleA(original_WinTitle), 0);
       clear();
       if(free_console_g)
       {
          WINAPITEST(FreeConsole(), 0);
       }
    }

  7. #97
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    So we need to make PEEK and POKE available. But we probably just need a section of memory available for the purpose.
    About this, from the original source it looks as if that is all he has done, but if you actually do want a contiguous play area it shouldn't be too difficult for sane STL implementations. (Some STL implementations do not correctly use the allocator passed to the template nor do they rebind the type.)

    A simple "increment the pointer" allocator partially specialized on a wrapper type, distinguishing between `int' and `wrapped<int>', passed to the relevant containers--including in this case the equivalent of `std::string'.

    Of course, using a simple map with a key of `LINE_NUMBER / 128' wouldn't be exactly difficult.

    Soma

  8. #98
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    >> Saves an if-statement and a str++. And gives a "SyntaxError" if the expression is not valid.

    Ah, yeah that works much better. I hadn't really dealt with the syntax error issue at all. I guess I should amend the other parsers as well.

    By the way, the internal use of longjmp kind of unnerves me. I know that it's probably safe, but couldn't it potentially cause problems if code with ctors/dtors is introduced?

    >> I've patched some 'init' and 'shdn' stuff as well as fixed the resize function--I think.

    Cool, I'll patch that in as soon as I have a chance. I was actually thinking we could rework all of the console I/O to make it more modular. I have some Win32 specific keyboard translation code laying around somewhere that could help with the input side.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  9. #99
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Ok, so the original usage of PEEK/POKE was to access such things as video memory, io ports, and operating system data structures, is that correct?

    >> A simple "increment the pointer" allocator partially specialized on a wrapper type, distinguishing between `int' and `wrapped<int>', passed to the relevant containers--including in this case the equivalent of `std::string'. Of course, using a simple map with a key of `LINE_NUMBER / 128' wouldn't be exactly difficult.

    Could you give a concrete example, please.
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  10. #100
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Could you give a concrete example, please.
    Scratch the allocator approach... of all the compilers I have only Comeau with Dinkumware seem to do the job exactly right. Everything else uses extensions that mess with the simple logic.

    As for the other approach, the original only allows for 32767 lines with 255 characters per line. That's about 8 MB. I was saying to split that into 512, or more, chunks. The first chunk would be passed, indifferently, to the allocators for the program logic. (This really is simple if you have an existing allocator.) The second chunk is the integer array. The next four chunks are the "freeplay" area. (The 64 KB memory area used by the original source.) The remaining chunks, are allocated on an as needed basis to store the program itself. Within `PEAK'/`POKE ' some simple modulo mathematics gives you which chunk to jump to where if it doesn't exist you allocate it. The same logic tells you where to process the current line.

    You could use a different storage approach, but this will probably use the least memory compared to many other approaches simply because of the way most BASIC programmers number the lines.

    Soma

    Edit: Yea, I really barfed on my original map example.

    Tossed source; horribly broken!
    Last edited by phantomotap; 03-19-2009 at 10:39 PM.

  11. #101
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    I don't know. I really don't see the point in sticking with those old, hard-coded limits. I would rather not put any unnecessary restrictions on the user at all. I would recommend allowing lines of any length and number, limited only by available memory. The same goes for the '@' array. It could easily be resized during accesses. As for PEEK/POKE, these could be treated much the same way. Start them out as empty buffers, and allow them to grow as necessary. No?
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  12. #102
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    O_o

    The remaining chunks, are allocated on an as needed basis to store the program itself.
    Nothing would stop you from allocating and spacing for additional chunks.

    Code:
    my_map[0] = new unsigned char[16384]; // divvy up for program logic
    // my_map[1] - my_map[65535] reserved for additions
    my_map[65536] = new unsigned char[16384]; // divvy up for integer array
    // my_map[65537] - my_map[131071] expands for larger array
    my_map[131072] = new unsigned char[16384]; // expansion array 1
    // ...
    my_map[1048576] = new unsigned char[16384]; // source area
    All I was saying, and my logic is still wrong, but I'm too sleepy to correct it, was that you could map 16384 byte chunks into play as you need. Further, I was suggesting a specific layout--which my broken logic failed to show.

    My first suggestion was to map the first few chunks into memory as program logic bits. (Allowing you to trash the system and blow your leg off just like in real programming.)

    My second suggestion was to map the next chunks into memory as the integer array. (There is nothing stopping you from mapping additional chunks as part part of the integer array.)

    My final suggestion was to map the first "source area" chunk, say 1048576, (6 in my other example) to lines 0-5-10-[...]-305-310-315; lines 1-6-11-[...]-306-311-316 to chunk 1048577; lines 640-645-650-[...]-945-950-955 to chunk 1048586; etc..

    It is just an odd layout for something like a sparse array of source lines implemented with maps and arrays.

    Yes, it has limits, but practicable ones. The integer array would allow for 1 GB. The "source array" limited by available memory. The line length only limited to 16384 characters. You could easily push these limits up by increasing the distance between the reserved areas or the chunk size.

    Edit: In case there is still some ambiguity, I don't care about *where* the limits are set. (They could even trivially be set during run time with compiler-like flags.) The limits only allow for a few tricks to simplify implementation, and they are rather efficient. Simply "growing" to fit every situation is wasteful. I don't like it. Now there is nothing wrong with using maps or sparse arrays everywhere if that's what you want to do, but I probably wouldn't do it that way.

    Soma
    Last edited by phantomotap; 03-19-2009 at 10:32 PM.

  13. #103
    Registered User
    Join Date
    Jun 2004
    Posts
    76
    The @ array can hold up to 2000 elements, and more if needed.

    Paul

  14. #104
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    I see what you mean. I guess I was just thinking of something a little more simple, along the lines of:

    vector< string > commands;
    vector< unsigned char > poke_buffer;
    vector< int32_t > array_buffer;

    Or perhaps mapping the last two buffers into one, so that poke_buffer[ 12 ] would be the byte located at array_buffer[ 3 ].
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  15. #105
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    The @ array can hold up to 2000 elements, and more if needed.
    >_<

    Yog Sothoth!

    I *HATE* crap like that.

    Can it hold "up to" 2000 elements? Or can it hold "more than" 2000 elements?

    They are different!

    Or perhaps mapping the last two buffers into one, so that poke_buffer[ 12 ] would be the byte located at array_buffer[ 3 ].
    Well, that's part of the rationale for my suggestion... where `poke_buffer[$]' may alter the source during execution, the interpreter, the integers, the integer array, and even the "play area" that it is supposed to poke.

    Soma
    Last edited by phantomotap; 03-19-2009 at 10:38 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Converting textBox1->Text into a basic string.
    By azjherben in forum C++ Programming
    Replies: 5
    Last Post: 06-07-2009, 08:27 PM
  2. [ANN] New script engine (Basic sintax)
    By MKTMK in forum C++ Programming
    Replies: 1
    Last Post: 11-01-2005, 10:28 AM
  3. what are your thoughts on visual basic?
    By orion- in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 09-22-2005, 04:28 AM
  4. VC++ 6 & MASM (eek)
    By cboard_member in forum C++ Programming
    Replies: 2
    Last Post: 07-16-2005, 10:00 AM