>> To make it "binary compatible" with the existing assembly code, I think reading documentation may not be sufficient.
Good point.
>> 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; }
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.
Yup, LOCATE is used to position to cursor anywhere on the screen.
Paul
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:
Saves an if-statement and a str++. And gives a "SyntaxError" if the expression is not valid.Code:void LET(const char *str) { int *ptr; if (GetVariableAddress(&str, &ptr)) { if (SkipSpaces(&str, '=')) { if (!EvalExpr(&str, ptr)) { SyntaxError(); } } } }
--
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.
Looking good.
Paul
^_^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); } }
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.)So we need to make PEEK and POKE available. But we probably just need a section of memory available for the purpose.
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
>> 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; }
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; }
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.Could you give a concrete example, please.
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.
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; }
O_o
Nothing would stop you from allocating and spacing for additional chunks.The remaining chunks, are allocated on an as needed basis to store the program itself.
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.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
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.
The @ array can hold up to 2000 elements, and more if needed.
Paul
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; }
>_<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!
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.Or perhaps mapping the last two buffers into one, so that poke_buffer[ 12 ] would be the byte located at array_buffer[ 3 ].
Soma
Last edited by phantomotap; 03-19-2009 at 10:38 PM.