Is this an easy conversion? Converting MINI-BASIC, written in MASM, over to C++? The Yahoo group for MINI-BASIC is here:
http://tech.groups.yahoo.com/group/minibasic/
Paul
Printable View
Is this an easy conversion? Converting MINI-BASIC, written in MASM, over to C++? The Yahoo group for MINI-BASIC is here:
http://tech.groups.yahoo.com/group/minibasic/
Paul
I should also note that MINI-BASIC uses the Windows API for display, keyboard and file I/O. This should be easy to convert to C or C++ using the stdlib.h
Paul
The original Palo Alto Tiny BASIC appeared in 1976 in the pages of Dr. Dobbs Journal. MINI-BASIC is a modern variant of Palo Alto Tiny BASIC using Windows API calls for screen, keyboard and file I/O. It has 26 variables, A-Z, and 1 array, @(I).
Despite these limitations, MINI-BASIC is a robust programming language written in ASM. More commands can be added with ease.
Paul
Not having looked at it, I'd say that rewriting it in C++ is an "intermediate" difficultly - far from impossible, but not a "5 minutes whilst browsing the web" either.
--
Mats
What should a person focus on when converting from one language to another?
Paul
First, you need to understand BOTH languages well. Next you need to have some way of testing the new code, to ensure that it still does the same thing as the old one.
I have looked a bit at the Mini-Basic code now, and it's using an awful lot of global variables and various naughty assembler tricks (like calls to the middle of a function), which makes the task a bit difficult.
--
Mats
Calls to the middle of a function? Could that be causing the occasional (and strange) Windows API error?
Paul
Looks good. Is that a mock screen, or the real thing?
Paul
It's real in the sense that I copied the MASM code into C++ - although it is more "assembler written in C" than C++, if you see what I mean.
I'm currently trying to get the input going - it's a bit long-winded to try to translate back from assembler, but I'm getting there. Once I have basic input working, I'll go to bed for today. Don't know when I will continue (and I don't know if I'll EVER finish it).
I've got over 600 lines, but that includes a bit of "comments of work to be done with untranslated assembler code".
--
Mats
Looks good. Keep up the good work. :)
Paul
Global variables are hard to keep track of, since it's hard to follow where such variables are being changed. I currently keep some global variables, but a lot of variables are passed around between functions.
And it's only 120KB of source code, so I wouldn't call it a "large" program.
How is your translation work going? Or are you waiting for me to do it for you?
--
Mats
Global variables are necessary, I think. I know they are hard to keep track of, but that is how programming can be sometimes. I don't know much C, so someone else would have to translate it to C.
Paul
Because I think it would run better, with fewer Windows API errors. In C or C++, there would be a standard library which could use the Windows API for handling the keyboard, display and file I/O.
As it is now, MINI-BASIC is a fragile piece of software. It crashes on occasion with Windows API errors. It is a mystery to me why this occurs.
Paul
It probably comes down to shoddy programming -- that is usually the case with so-called fragile software. Fragile = badly written. Being written in ASM only compounds these problems when somebody is using the language without a complete and thorough understanding of internal machine code.
If you were to convert or 'port' it, I would suggest that you just rewrite it from scratch. ASM - C++ is, if you ask me, a waste of time.
Note, though, that C++ is only based on C... it is not C. (I know that's going to start an argument but I'm quoting Bjorn Stroustrup himself). You would need to choose one or the other. C++ is a lot easier when it comes to dealing with errors and the STL names it almost trivial to write an interpretter.
I would say, however, that if you're not familiar with C/C++ you should probably start with a project a little smaller than that.
So is it possible to convert to C++?
Paul
It is possible to implement it from scratch (write a program that does the same thing functionally). I don't think there is any direct way to convert asm to C++ or C, since it is lacking all the higher level constructs. Assembly just contains less human readable information than the equivalent C or C++ code.
Writing it from scratch in C would require examining how the program works first.. I believe anyway.
Paul
And converting Assembly to C would require you to study how the program works (what is input, what is output) as well as the implementation details of the assembly version. The latter you wouldn't have to do, since you'd just design that yourself any way you like.
Right, I've done some work, and I can probably say that I wouldn't be surprised if the API errors you get are caused by global variables being changed when some other bit of code expects it to remain the same.
As to wheter C or C++ or Assembler is "more safe", I'd say all three languages are capable of doing the wrong thing, and badly written code in either of them will not work reliably.
--
Mats
Yeah. That's true. I don't know how badly written MINI-BASIC is, but it does crash occasionally with Windows API errors. Which is unfortunate. Translating it to C or C++ shouldn't be impossible. It has a very limited subset of the traditional BASIC language. It has 26 scalar variables and 1 array -- denoted by @(I).
Paul
Sure. But it's not the easiest way to "translate" it.
I'm still working my way through it, and I'm just about to implement the begginnings of the parser.
Most of the code so far is following very closely the original code, but I will use C style strings in the parser, to make life a bit more C-friendly.
--
Mats
Ok, thanks for the update. :) Sounds good so far.
Paul
True... or you could take a BASIC standard approach and write a new interpretter using BASIC syntax.
I think you're best bet is to just write a new basic interpretter or use an existing open-source interpretter.
It looks like you're getting a bit of help with it though so good luck!
Yeah, I think it would work that way. Hopefully it can work.
Paul
Well, I'm just about ready to try the "list" command. Currently it doesn't quite work (says Syntax error), but I should have that fixed in minutes.
I completely changed the way the actual program is stored - I'm using a linked list rather than a fixed area of memory for the program store.
It's still a LONG way off working, but it's getting in the right direction.
Next, I think will be save and load, so that I can start using some saved code to try things out, rather than having to type it all in.
Edit: changed wording and added a bit of what I'm doing next.
--
Mats
Sounds good! Thanks for the update.
Paul
Just done a load and save function, and VERY basic (pun intended!) testing.
Picture for proof (check the URL for "proof" [it's either that, or I'm spending a lot of time doing photoshopping! ;) ]).
Note that the program is absolutely not valid basic, but just something I typed in before doing save.
Edit: I just loaded pitman.mbi as well - it seems to work so far as I can tell. Just paused it a bit into the listing.
--
Mats
Hey, that's pretty cool, matsp. Would you mind sharing what you have so far? I'd be interested to see what approach you've taken with this. Maybe some of us could pitch in on whatever else needs to be done, to make things a little easier? Just a thought.
I'd be happy to share it, but bear in mind that I'm following the original format/functions and it's a bit messy and hacky.
Once I've got something roughly working (at least a small section of the functionality of the language, such as assigning variables, goto's, gosub's and for-loops working), I'm thinking that I should do a bit of a refactoring, and use more C++ style approach, rather than "Assembler written in C", as it is now.
I don't think that will happen for a few weeks yet.
And I'd rather share something that I'm reasonably proud of, than a "quick hack".
--
Mats
Well, I figured it would be pretty hackish, given what you have to work with. But I'm game. I don't really have a lot of (practical) assembly experience, but there are other areas I could be of some use. And I don't mind working in straight C, either - it doesn't have to be very fancy, it just has to work, right? Either way, keep us posted on the progress. It looks like an interesting project.
I may put something up tonight - I'm not doing any of this at work, you know... ;-)
--
Mats
No rush - I don't expect this to become your second job, either!
Looks nice. I think it would work well in C++. I don't know about speed-wise, if it would be faster, but hopefully those Windows API errors would be less frequent.
Paul
Yeah, Windows API errors were frequent in the assembled version.
Paul
Just thought I'd share that "run" now works too - it only supports ONE command within the code (PRINT, and only with quoted strings as argument), but it's a move in the right direction.
--
Mats
>> ...but I think porting it to C/C++ would benefit the C/C++ community.
How exactly does a C/C++ programmer benefit?
>> It is a useful language modeled after Palo Alto Tiny BASIC from 1976.
Debatable. I once had the task of translating a legacy program written in BASIC to C. The problem was that over the years, with each modification, the program had developed more and more bugs, and ran ever slower. The program consisted of at least 10,000 - 15,000 lines of code, and the maintainers openly admitted that they had problems understanding the code themselves (which they had written!), and were not at all sure what was causing all of the errors. After reading the code for a few hours it became clear to me that a complete rewrite would be necessary. Tracing the flow of the code was nearly impossible, and global variables were strewn all over the place. A real mess. Incidentally, the C version took only a few days to complete, was orders of magnitude smaller, and ran much faster. The bottom line is that BASIC just isn't very scaleable. It may be 'useful' for certain toy projects, but falls way short of being a 'real' programming language.
Tiny BASIC is a minibasic compared to normal, classic interpreted BASIC. It supports a minimal amount of commands and structures...26 scalar variables and 1 array (denoted by @(I)).
Paul
That would indicate that it is useless for almost any practical purpose. On the other hand it might be a good exercise in implementing a programming language.
It is a good exercise in implementing a programming language. It has only the barest of commands...from the MiniBas.txt document:
"In MINI-BASIC, all number are 32 bits signed integers and must be within the range of -2 147 483 647 .. 2 147 483 647.
Variables
There are 26 scalar variables donoted by the letters A through Z. The one array variable is denoted by '@(I)'. Its dimension is originally limited to 2000 cells in source code, but it can be extended to hundreds thousands of cells if you really need it.
Functions
There are four functions in MINI-BASIC.
ABS(X) - Returns the absolute vaulue of the variable X.
PEEK(X)- Returns the contents of 8 bits memory location X in Basic text area.
RND(X) - Returns a random number between 1 and X (inclusive).
SIZE - Returns the number of bytes left unused by the program
Arithmetic and Comparison Operators
The following operators are supported:
/ - integer divide (fractional results not returned)
* - integer multiply
- - subtract
+ - add
> - compare if greater than
< - compare if less than
= - compare if equal to
NOTE: multiple assignment statements are not supported, i.e., "A=B=O" is interpreted by MINI-BASIC as meaning "set A to the result of comparing B with 0
<> - compare if not equal to
>= - compare if greater than or equal to
<= - compare if less than or equal to
The +,-,*, and / operations return a value within the range -2 147 483 647.. 2 147 483 647.
All compare operations result in a 1 if the comparison is true and a 0 if it is false.
Expressions
Expressions are formed with numbers, variables, and functions with arithmetic and compare operators between them. + and - signs can also be used at the beginning of an expression. The value of an expression is evaluated from left to right, except that the * and / operators are always given precedence, with + and -, and then the compare operators following, in that order.
Parentheses can be used to alter the order of evaluation in the standard algebraic sense.
Statements
A MINI-BASIC statement consists of a statement number between 1 and 99999 followed by one or more commands (see Commands below).Commands in the same statement are seperated by a colon ":".If the "GOTO", "STOP", and "RETURN" commands are used then they must be the last command in that statement.
Program
A MINI-BASIC program consists of one or more statements. When the direct command (see Direct Commands below) "RUN" is issued, the statement with the lowest statement number is executed first, then the one with the next lowest statement number, etc. The "GOTO", "GOSUB", "STOP", and "RETURN" commands can alter this normal sequence. Within any statement the execution takes place
from left to right. The "IF" command can cause remaining commands within the same statement to be skipped.
Blanks
MINI-BASIC statements and commands may use blanks freely, except that numbers, command key words, and function names may not have embedded blanks.
Editor
MINI-BASIC contains a useful line editor for entering and correcting MINI-BASIC programs. Maximum line length allowed is 255 char. All of the line editing features of a standard editor are used :
- Right and left arrows to go right and left in the edited line
- Home and End keys move cursor to top or end of line
- Del and BackSpace keys to cancel a char. preceding or following cursor position
- Insert to switch between normal and insert mode. Insert is the mode by default, and cursor appearence is modified, depending of the mode used.
- Alt+num. sequence to enter an ASCII code character
The EDIT nn command allows to correct an existing MINI-BASIC statement. Statements may be deleted by simply typing their statement number, followed by a CR. Corrections may be verified by typing LIST nn and striking the Escape to terminate the LIST process.
ERROR MESSAGES
There are 23 error messages in MINI-BASIC. When an error is encountered the error message itself is printed, followed by the statement causing the program error. Control is then passed to the MINI-BASIC editor.
STATEMENT COMMANDS
MINI-BASIC statement commands are listed below with examples.
Remember that commands can be concatenated with semi-colons. In order
to store any given statement, you must precede that statement with a
statement number between 1 and 32767. Statement numbers are NOT shown
in the examples.
LET command
LET A=234-5*6;A=A/2;X=A-100;@(X+9)=A-1
The LET command assigns the value of an expression to the
specified variable. In the example above, the variable "A" assumes
the value of the expression "234-5*6", or "204". Then the variable "A"
assumes the value "102". Next, the variable "X" is set to the value of
the expression "A-100", or "2". The last command assigns the value
"101" to the array variable "@(11)". The "LET" portion of the LET
command is optional, i.e., the following examples are true:
A=10
C=5*3/5;C=C*5
REM Command
REM ANYTHING CAN BE WRITTEN AFTER "REM"
The REM command is ignored by TINY BASIC. It is used by
experienced programmers to comment BASIC programs. A program comment
is used by programmers to remind themselves of the logic of a program
section. All good programs are invariably commented.
PRINT Command
PRINT will cause a carriage-return (CR) and a line-feed (LF) on
the output device.
PRINT A*3+1,"ABC"
This form of the PRINT command will print the value of the
expression A*3+1 on the output device, followed by the string ABC on
the same line. Note that single (') or double quotes (") may be used
to denote character strings, but that pairs must be mached.
PRINT A*3+1,"ABC",
This form of the PRINT command will produce the same results as
the previous example except that the normal CR-LF is inhibited by the
trailing comma at the end of the statement. This allows other PRINT
commands to print on the same line.
PRINT A,B,#3,C,D,E,#10,F,G
This form of the PRINT command demonstrates format control. The
format character # is used to indicate the number of leading spaces to
be printed before a number. The default number is 6. Once the # format
is invoked it is active for the remainder of the statement unless
overridden by a subsequent format specifier, as in the example.
PRINT 'ABC',\,'XXX'
The back-slash (\) character is used to cause a CR without a LF.
In this example, the string ABC is printed followed by the string XXX
on top of the original ABC.
INPUT Command
INPUT A,B
The INPUT statement is used to acquire input data during program
execution. In the example above, MINI-BASIC will print A: and wait
for a number to be typed at the console terminal. Next, MINI-BASIC
will print B: and wait for another number to be typed at the console
terminal. In this example the variables A and B will assume the values
of the appropiate input values. The INPUT statement will accept
expressions as well as numbers as input.
INPUT 'WHAT IS THE WEIGHT'A,"AND SIZE"B
In this example MINI-BASIC will print the string WHAT IS THE
WEIGHT: and wait for operator input. Next, the string AND SIZE: will
be printed, on the same line, and MINI-BASIC will wait for operator
input.
INPUT A,'STRING',\,"ANOTHER STRING",B
MINI-BASIC will react to the back-slash character (\) in this
example in the same fashion as in the PRINT command. The second string
will overwrite the first string STRING.
IF Command
IF A<B LET X=3;PRINT 'THIS STRING'
The IF command works with the comparison operators (enumerated
above) to check the validity of the specified comparison condition. In
this example, if the comparison A<B is true, then the balance of the
commands in the statement are executed. However, if the comparison
tests false, then the balance of the commands in the statement are NOT
executed and control passes to the statement with the next highest
statement number.
IF A<B GOTO 100
This example illustrates a common use of the IF command and the
GOTO (see below) command. If the comparison tests true control is
passed to statement number 100, otherwise execution passes to the
statement with the next highest statement number.
GOTO Command
GOTO 120
This statement is used to modify the normal sequence of
execution of MINI-BASIC statements. In this example, control is passed
unconditionally to statement number 120. The GOTO command cannot be
followed by a semi-colon and other commands within the same statement.
It must appear as the last command in any given statement.
GOTO A*10+B
This form of the GOTO is called a "computed GOTO". In this case,
control is passed to the statement number represented by the
expression that follows "GOTO".
GOSUB Command
GOSUB 120
The GOSUB command is used to invoke a subroutine at the
specified statement number (120 in the example). Control is passed to
statement number 120 and execution continues. A RETURN command (see
below) is used, within the subroutine, to cause MINI-BASIC to pass
control to the statement that immediatly follows the GOSUB command
that caused the subroutine to execute. The GOSUB command cannot be
followed by any other commands within the same statement and it must
be the last command within any given statement. GOSUB commands can be
nested, limited by the size of the stack space (see below).
GOSUB A*10+B
In this example, the subroutine at the statement number equal to
the value of the expression is executed. This form of the statement
will cause a different subroutine to be executed depending upon the
value of the expression that follows "GOSUB".
RETURN Command
RETURN
The RETURN command causes execution to resume at the statement
that follows the GOSUB that caused the current subroutine to be
executed. It must be the last command of any given statement.
FOR Command
FOR X=1 TO 10
PRINT 'HELLO'
NEXT X
The FOR command is used to set up execution loops. In the TINY
BASIC program segment above the statement PRINT 'HELLO' is executed 10
times since it is placed between the FOR statement and the NEXT
statement. The NEXT X statement (see below) has the effect of
incrementing X by one and passing control to the FOR statement. If the
new value of X is still less than or equal to 10, the MINI-BASIC
statements between FOR and NEXT are executed again. This process
repeats until X is incremented past the loop termination value (10 in
the example above).
FOR X=1 TO 10 STEP 2
PRINT 'HELLO'
NEXT X
In the above variant of the FOR command the loop increment has
been changed from 1 (the default) to 2 by means of the STEP clause. In
this case, the program fragment would only print HELLO five times if
executed.
FOR commands can be nested, that is, one FOR loop can contain
other FOR loops provided that the loop variables (the variable X in
the examples) are diferent,. If a new FOR command with the same loop
variable as that of an old FOR command is encountered, the old FOR
will be terminated.
NEXT Command
NEXT X
The NEXT command is part of the FOR command and is used to cause
loop variables to be incremented by the increment specified by the
STEP clause (default is 1) and to pass control to the appropiate TINY
BASIC FOR loop. The variable specified by the NEXT command (X in the
example) is used to specify the correct FOR loop.
POKE Command
POKE A,B
The POKE command is used to place data B into memory address A.
This command may be repeated as follows:
POKE A,B,C,D
In the above example, data B is placed in memory location A, then data
D is placed in memory location C. All variables may be expressions. Be
careful not to POKE MINI-BASIC itself!
USR Command
USR(I,J)
The USR Command is actually a built-in TINY BASIC subroutine
call that permits linkage to machine language subroutines. All 8086
registers are available for use by the machine language subroutine. It
is the responsibility of the machine language routine to execute a RET
instruction. In the example above, a machine language routine at
address I is called. J is an optional parameter that, if present, will
be passed in register BX to the subroutine.
WAIT Command
WAIT I,J,K
The WAIT command is used to cause MINI-BASIC execution to pause
and wait for a specified value at an 8086 input port. In the example
above, the value at input port I is read, exclusive OR'd with the
value of the expression J, and the result is then AND'd with the value
of expression K. WAIT will return only if the final result is
non-zero. WAIT provides an easy-to-use mechanism to cause TINY BASIC
to pause its execution and wait for a specified external event. J is
assumed to be 0 if not specified.
STOP Command
STOP
This command stops the execution of a TINY BASIC program and
passes control to the MINI-BASIC monitor. It can appear many times in
a program but it must be the last command in any given statement.
DIRECT COMMANDS
Direct commands are those commands that can be invoked only by
the operator when MINI-BASIC is in command mode (i.e. in response to
the '>' prompt). All statement commands (those listed above) can be
invoked while in command mode. Typing a control-C while in command or
monitor mode will cause TINY BASIC to terminate. Control is then
passed to the host operating system monitor.
Recall that a statment consists of a statement number followed
by one or more commands. If the statement number is missing, or if it
is 0, the command will be executed immediatly after typing the
terminating CR. The following commands can be used as direct commands;
they CANNOT be used as part of a MINI-BASIC statement.
RUN Command
RUN
The RUN command causes execution of the stored TINY BASIC
program. Execution will commence at the lowest numbered statement and
continue until there are either no more statements to execute or a
STOP command is found. A long MINI-BASIC program may be terminated by
typing control-X at the console. This passes control the the TINY
BASIC monitor. A control-C may be typed at any time also, then TINY
BASIC is terminated and control is passed to the host operating
system.
LIST Command
LIST
The LIST command is used to display the current TINY BASIC
program on the operator's console. The statements will be listed in
numerical order. If LIST is followed by an expression (e.g. LIST 200)
the listing will commence with statements following the specified
statement, inclusive.
NEW Command
NEW
The NEW command deletes the current program from TINY BASIC's
memory.
SAVE Command
SAVE FILENAME
The SAVE command saves the current TINY BASIC program on the
logged in disk with the specified filename FILENAME. The default
filetype is ".TBI". If there is insufficient room on the disk, the
SAVE command responds with "HOW?".
LOAD Command
LOAD FILENAME
The LOAD command loads the specified MINI-BASIC program from the
logged in disk into the program area. Any program residing within TINY
BASIC prior to the LOAD operation is lost. If the specified program is
not found on the disk, or if there is insufficient room for the
program, LOAD responds with "HOW?". The filetype is assumed to be
".TBI".
BYE Command
BYE
The BYE command terminates MINI-BASIC. Control is passed back to
the host operating system.
MINI-BASIC OPERATION
MINI-BASIC is initiated from the host operating system's command
mode like any other transient command. TINY BASIC will sign-on,
announce 'OK', and then prompt '>' awaiting operator interaction. An
example follows:
A:TBASIC
8086 MINI-BASIC V1.0
OK
>
In the example above the program 'TBASIC.COM' was found on the
logged-in disk ('A' in the example). TINY BASIC then commenced
execution by first announcing itself and then prompting '>' for
operator input.
MINI-BASIC utilizes all of the host operating system's line
editing facilities. For example, if an operator wished to cancel a
line typed to MINI-BASIC, he need only type a control-X, etc. If hard
copy of a MINI-BASIC session is desired, control-P and control-N will
toggle the printer, if it exists.
At present, saved MINI-BASIC programs can be edited only with
the internal TINY BASIC editor. Programs prepared by an external
editor can not be read by MINI-BASIC."
Paul
Ok, thanks.
Paul
I have vague plans to allow loading of basic source-code that is plain text - there is no reason why this CAN'T be supported. This will also support editing using an external editor.Quote:
At present, saved MINI-BASIC programs can be edited only with
the internal TINY BASIC editor. Programs prepared by an external
editor can not be read by MINI-BASIC.
--
Mats
Source code.
Please bear in mind that this is work in progress, and a lot of it follows the original minibasic source code, where a better solution could be made in C or C++.
The file is minibasic.cpp, but it's actually more C than C++.
--
Mats
Edit: Added attachment - it is really a .zip file, not a text file.
Hmm, I don't see the attached file, matsp...
Edited.
--
Mats
For some reason, ScrollConsoleScreenBuffer returns an error on my system. I'll post an update as soon as I find out why...
The problem is that 'EditSize' isn't always compatible with the user's virtual console metrics. This workaround seemed to fix it:
Code:void RealRedimWindow(COORD *size)
{
COORD maxSize;
int newSize, oldSize;
TestApiError(GetConsoleScreenBufferInfo(hStdOut, &csbi));
maxSize = csbi.dwMaximumWindowSize;//GetLargestConsoleWindowSize(hStdOut);
*size = maxSize;
if (size->X > maxSize.X || size->Y > maxSize.Y)
{
BadParameter();
return;
}
oldSize = csbi.dwSize.X * csbi.dwSize.Y;
newSize = size->X * size->Y;
csbi.dwSize = *size;
if (newSize == oldSize)
{
return;
}
// Current buffer is higher, redim window first, then buffer
if (newSize < oldSize)
{
TestApiError(SetConsoleWindowInfo(hStdOut, 1, &csbi.srWindow));
TestApiError(SetConsoleScreenBufferSize(hStdOut, csbi.dwSize));
}
else
{
TestApiError(SetConsoleScreenBufferSize(hStdOut, csbi.dwSize));
TestApiError(SetConsoleWindowInfo(hStdOut, 1, &csbi.srWindow));
}
ConsoleCharNbr = newSize;
TestApiError(GetConsoleScreenBufferInfo(hStdOut, &csbi));
TestApiError(SetConsoleMode(hStdOut, 0));
TestApiError(SetConsoleMode(hStdIn, ENABLE_PROCESSED_INPUT));
ClrScr();
}
So how's it coming along?
Paul
Well, the original problem I was having was that my console apparently doesn't support a size of 100 * 25, but the code wasn't detecting that. Reading the documentation for 'GetLargestConsoleWindowSize', I found this:
So then I changed the line to:Quote:
The function does not take into consideration the size of the screen buffer, which means that the window size returned may be larger than the size of the screen buffer. The GetConsoleScreenBufferInfo function can be used to determine the maximum size of the console window, given the current screen buffer size, the current font, and the display size.
That seemed to work, since now I was getting a 'BadParameter' error. So I set 'size' to 'maxSize', and the program ran without errors.Code:maxSize = csbi.dwMaximumWindowSize;//GetLargestConsoleWindowSize(hStdOut);
But now that I think of it - why resize the window at all? This seems to work fine:
Code:void RedimWindow(const COORD *)
{
TestApiError(GetConsoleScreenBufferInfo(hStdOut, &csbi));
ConsoleCharNbr = csbi.dwSize.X * csbi.dwSize.Y;
TestApiError(SetConsoleMode(hStdOut, 0));
TestApiError(SetConsoleMode(hStdIn, ENABLE_PROCESSED_INPUT));
ClrScr();
}
First of all, you may want to modify it so that it doesn't try to change the window to a size not supported, instead of giving "bad parameter".
Whether it is valuable to change the size or not is a different matter. I kept it the way the original code was - the principle being "Make it work first - then change it around".
I have made changes - e.g. the way that lines are kept in memory as a linked list instead of as a big chunk of text (with binary line numbers in it) is a change from the original format. In a proper C++ solution, I think a std::map<int, string> would be the right solution, but linked list was easy to implement at the spur of a moment.
--
Mats
I'm working on 'EvalExpr' right now, and I think it's almost done. I'll post an update soon...
Cool. Thanks.
Paul
Hmm, I'm getting seg-faults with the PRINT routine. Any ideas, matsp?
Ah, it's because when 'NextLine' was called in 'direct' mode, BasicLine* 'current' was NULL. These changes seemed to fix the problem:
Code:void DirectCommand(const char *LineBuffer)
{
current = 0;
Execute(LineBuffer, tab1);
}
void NextLine(void)
{
if(current)
current = current->next;
if (!current)
{
Prompt();
}
}
That looks like a good change - although changing current in DirectCommand may not be the right thing, as I think you should be able to stop the code, then continue after for example printing values of variables.
--
Mats
I see what you mean. Well then how about:
Code:void DirectCommand(const char *LineBuffer)
{
const BasicLine* saved = current;
current = 0;
Execute(LineBuffer, tab1);
current = saved;
}
I agree. I think that's a good change. It looks good.
Paul
So what is the difference between ':' and ';' when separating commands?
I think : is for how MINI-BASIC separates commands. I'm not sure how C++ does.
Paul
Minor fix: changed the 'while' loop in DispNumber to a 'do/while' loop so that zero is displayed:
Code:do
{
*ptr-- = (n % 10) + '0';
size--;
n /= 10;
}
while(n);
>> I think : is for how MINI-BASIC separates commands.
But looking at the doc you posted, it has:
Quote:
LET A=234-5*6;A=A/2;X=A-100;@(X+9)=A-1
I think that's an error in the document. Let me post some example code really quick:
Windows Console Tiny Basic V.1
Ready.
>load castle
Ready.
>list
Ready.Code:5 CLS
10 PRINT "MINI-BASIC ASCII Art":PRINT
15 PRINT"|---| |---| |---| |---| |---| |---|"
20 PRINT"| --- --- | | --- --- |"
25 PRINT"\ / \ /"
30 PRINT" | | | |"
35 PRINT" | | |---| |---| |---| | |"
40 PRINT" | |-- --- --- --| |"
45 PRINT" | |"
50 PRINT" | |"
55 PRINT" | |"
60 PRINT" | /------------------\ |"
65 PRINT" | |[][][][][][][][][]| |"
70 PRINT" | |[][][][][][][][][]| |"
75 PRINT" | |[][][][][][][][][]| |"
80 PRINT" | |[][][][][][][][][]| |"
85 PRINT" | |[][][][][][][][][]| |"
90 PRINT" -------------------------------------------------"
95 PRINT"Castle by dunric"
>
Notice the ":" in line 10. So it has to be ":" and not ";". Otherwise it would be a syntax error.
Paul
Got it.
Ok, so this update contains the implementation for 'EvalExpr', and the 'LET' command, plus some other minor changes. Let me know if I broke anything. :D
Paul, just thought I'd let you know that it still has a way to go before you'll be able to test it out, but it is coming along...
Ok, thanks Sebastiani.
Paul
I took a look at the code... the original is a bad dog.
Before I jump into this, which I may if time allows, may I at least suggest updating the API test to do something useful?
Soma
Code:void mbox_error_handler
(
const std::string & from_f,
const std::size_t where_f,
const bool show_f
)
{
if(show_f)
{
const DWORD buffer_length(256);
char message_buffer[buffer_length];
const DWORD characters_written = FormatMessageA
(
FORMAT_MESSAGE_FROM_SYSTEM,
0, // lpSource (ignored here)
GetLastError(),
0, // dwLanguageId (use system defaults)
message_buffer,
buffer_length,
0 // Arguments (pointless VA_LIST nonsense)
);
std::ostringstream message;
if(0 == characters_written)
{
message
<< "File: " << from_f << '\n'
<< "Line Number: " << where_f << '\n'
<< "Error Message:\n"
<< "unknown exception"
;
}
else
{
message_buffer[characters_written - 2] = 0;
message
<< "File: " << from_f << '\n'
<< "Line Number: " << where_f << '\n'
<< "Error Message:\n"
<< message_buffer
;
}
MessageBoxA
(
0, // hWnd (no owner)
message.str().c_str(),
"WIN32API Exception",
MB_ICONERROR | MB_OK | MB_TASKMODAL
);
}
}
#define WINAPITEST(BADVALUE, RESULT) mbox_error_handler(__FILE__, __LINE__, BADVALUE == RESULT)
>> I took a look at the code... the original is a bad dog.
I couldn't agree more.
>> may I at least suggest updating the API test to do something useful?
That's a good idea. And since API errors are basically fatal, you might as well just have the program die at that point.