Closed rpocc closed 4 years ago
Hi!
Thank you for developing this great IDE! Your totally modern and alternative approach to BASIC really reinvents the whole idea of high-level programming for 8-bit. It doesn't have so much optimized commands as Turbo BASIC but anyway, the numberless coding for 8-bit is a fantastic improvement. I keep my eye close on this project and would like to express my big respect!
Thanks!
I admit that I have not spent much time improving the Atari IDE, as much of the requests I receive are from people doing development on PC or using other editor, so lets see your suggensitons:
I've tried to make a complete application with it and find several things, which I miss during development.
- Line delete and instant jump to a given line number. I really miss line copy and paste but the first two could be even more helpful because it takes the time to navigate between a far proc and a header section.
Line delete is the standard SHIFT-DELETE key, I could implement SHIFT-INSERT easily, it is the same as CONTROL-A, ENTER, UP.
For go-to-line number, using CONTROL-G seems right?
Copy and paste is difficult, as I don't currently have a buffer to store copied text, only a buffer for current line.
What I could do is a key that remembers a line and then another that inserts a copy of that line in the current position, to do that I could store the memory address of an edited line. CONTROL-M (mark) and CONTROL-C (copy) ?
- A documented memory location which can be tested from the working code to tell whether it's executed under IDE or as a stand-alone program. This could help placing program break conditions in the code without removing it each time before compilation.
That is not as easy as it seems because the program is written without fixed memory locations - at compile time the linker decides where to put each piece of code.
One possibility is using the value returned by "FRE()", as the value should be much lower when your program runs in the IDE. This is simple, but does not work if a user runs your program in a computer with less RAM.
A more complicated option is using the interpreter pointer (this is like the program-counter of the interpreter) and test if it is greater than a give value, then you are under the IDE. The pointer is currently at ZP locations $85 and $86. Problem is, if your program is big, the code could be bigger even outside the IDE.
Currently, in FBI, the IDE uses memory up to about $4140, then there is the program text and after that you have your program code, so you could assume that the code pointer is always > $4150 when run from the IDE.
See this program:
? DPEEK($85)
It writes 16752 from the integer IDE, 10474 compiled from the IDE to disk, 8648 compiled with the cross-compiler.
- Bitwise shift commands. This command is native to the processor and could improve execution time of frequently used power of 2 divisions and multiplications use3d in processing of graphics.
I could implement simple shift operators, but I'm not sure about the syntax. Currently the interpreter has two shift tokens, used internally, a simple shift-left by 2 and another shift-left-by 8. The first is used at array access, as the index must be multiplied by 2 to get the memory address, the second is used for SOUND, PMADR, XIO and OPEN.
And there is another question - for right-shifts, ¿do you want arithmetic or logic shift?
I recommend that you create a new enhancement request with only this, so we can discuss the syntax and options.
- LOCATE command or equivalent function. It can be emulated via proc (which I've done along with area fill but it runs too slow.
There is a LOCATE command in the current version! It is used the same as the Atari BASIC one.
- Of course I also miss lot of familiar commands both from standard and Turbo basic and hope that sometimes the most popular of those will be implemented but I understand that it will make the IDE occupy more memory, so I'm not asking to implement everything.
If you have specific requests, please create a separate issue with that, so I can track it.
Some of the graphics commands of TurboBasic XL are not that useful (IMHO), and there are ones like the flood-fill that are really complicated - to be as fast as possible there is a custom fast PLOT routine for each Atari graphics mode, and the code is about 1kB.
And by the way, is there any chance that some time in the future you will implement stack-operated function calls? Currently I've found a way for processing recursive code, using global temporary variables for data interchange between procedures and some array-emulated stack, but it's so complicated and so slow that becomes impractical.
Problem is that the FastBasic stack is really small, so I don't want to add stack operated calls, but you can call procedures recursively (and the cross compiler even optimizes tail-calls to jumps).
Again, it is better to discuss this with specific examples.
Meanwhile, since we are playing by rules of not using line numbers, labels and non-conditional jumps, implementing stack and functions could be super-helpful for keeping the code structured and readable. Sometimes I still miss labels and good old GOTO.
I plan to implement PROCedures with parameters, to simplify code with a lot of procedures, but functions are really difficult because the way the compiler generates code.
When you write an assignment like "A(3) = B + 5" the compiler calculates the address of the expression "A(3)", and stores it in a special register, like "SADDR = ADR(A) + 3 2". Then, the rest of the line calculates the expression "B + 5" and at the end the value is stored to the SADDR location. It is like the code transforms to "SADDR = ADR(A) + 3 2 : DPOKE SADDR, B+5 "
In fact, the code generated for "DPOKE ADR(A) + 3 * 2, B+5" is exactly the same as the code generated for "A(3) = B + 5".
This is simple - as the address to store calculation is separated from the assignment - and also fast, because the store is always to an address in a ZP location, but has a problem. If you could write user-defined functions, any POKE, DPOKE or array store in the function would overwrite the value in SADDR, and make the code invalid.
Note that originally FastBasic used the stack for storing the addresses, but this was a lot slower because the 6502 code was bigger for each POKE and DPOKE.
Thanks.
You are welcome!
Thank you for so fast reply!
Line delete is the standard SHIFT-DELETE key, I could implement SHIFT-INSERT easily, it is the same as CONTROL-A, ENTER, UP. For go-to-line number, using CONTROL-G seems right?
Speaking of a mnemonics, G(o), J(ump) or L(ine) should be ok. I also should mention that your implementation of PgUp and PgDown is quite slow when running on original speed because it has to animate the scroll many times. Maybe, if you going to implement an arbitrary line jump, maybe it's better to make the page change operation based on the same routine as well?
Copy and paste is difficult, as I don't currently have a buffer to store copied text, only a buffer for current line. What I could do is a key that remembers a line and then another that inserts a copy of that line in the current position, to do that I could store the memory address of an edited line. CONTROL-M (mark) and CONTROL-C (copy) ?
I could suggest another way. If you can dedicate a buffer for a single line, it means that copy-paste operations can be pipelined in a loop. If so, there can be two markers, start line and the end line which can be set by Ctrl-, and Ctrl-. (discussable) and operations like Ctrl-M(ove) and Ctrl-C(opy). It will take some extra code to detect the direction of movement for correctly modify the line numbers for movement and copying forward and backward but in exchange this could end up in nearly the only existing programming IDE for Atari with multi-line copy-paste and cut-paste operation.
That is not as easy as it seems because the program is written without fixed memory locations - at compile time the linker decides where to put each piece of code.
Well, since almost any computer system provides some registers for user-defined use, the IDE could set it's own special value to one of the following memory locations which are described in "Mapping the Atari" book: 588,589 24C,24D PPTMPA, PPTMPX One-byte temporary storage registers for relocatable loader. 590-618 24E-26A Spare bytes, reserved for future use. 651 28B IMASK Unused. 652 28C JVECK Temporary jump vector; unused.
I could implement simple shift operators, but I'm not sure about the syntax. I recommend that you create a new enhancement request with only this, so we can discuss the syntax and options.
OK, I will do it.
There is a LOCATE command in the current version! It is used the same as the Atari BASIC one.
Oops, but this command isn't listed in the manual! Is there some updated manual version or a file where I can see the full list of implemented commands and operators? The same is with locations marked by @ character. I saw lot of them in your BAS code and it looks like named defines but I haven't find any information on using that character and the list of built-in symbols (I mean named locations).
to be as fast as possible there is a custom fast PLOT routine for each Atari graphics mode, and the code is about 1kB.
Great, where can I read more about it?
I don't want to add stack operated calls, but you can call procedures recursively (and the cross compiler even optimizes tail-calls to jumps).
Oh yes, I really noticed that because what I was exactly doing is implementing the closed loop fill function. But really with the need of maintaining the stack pointer manually, it's much easier to make a serious logical error.
I plan to implement PROCedures with parameters, to simplify code with a lot of procedures, but functions are really difficult because the way the compiler generates code.
Currently I code emulated behaviour of functions this way (which probably is similar to what as I can see in your IDE source code):
tempA=1:tempB=2:retvalue=0:exec routine
if retvalue
exec routine2
else
exec routine3
endif
'
proc routine ' example
if tempA=tempB
retvalue=1 ' return value assignment 1
endif
' else: return value remains at its default state
endproc
Probably, some function defining syntax can be convertible to a form similar to one described above. I see advantage of this approach in possibility to include generation of local variables, although this may be dangerous without storing them in a stack and just leaving at some arbitrary temporary memory locations.
Look what i've found. Another location directly dedicated for storing the BASIC interpreter status (but for ROM): 1016 3F8 BASICF Shadow of current status of BASIC. Zero means ROM BASIC is enabled; nonzero means it's not. Must be in sync with disabling of ROM BASIC. To disable BASIC, set BASICF to nonzero, then do a warm start (press RESET); DOS will load and tell you there is no cartridge present when you try to return to BASIC.
I've tested this location under different states of the system and emulator. It shows the following values: 0: ROM BASIC enabled (booted or not booted, with or without OS) 1: ROM BASIC disabled (booted or not booted, with or without OS) 70: Shows when it's booted from the Fastbasic ATR and doesn't count if ROM BASIC is enabled or disabled. In fact, it seems like this feature is already utilized by the OS you've used as a host for the FB but maybe IDE could put another bit in there.
Thank you for so fast reply!
Line delete is the standard SHIFT-DELETE key, I could implement SHIFT-INSERT easily, it is the same as CONTROL-A, ENTER, UP. For go-to-line number, using CONTROL-G seems right?
Speaking of a mnemonics, G(o), J(ump) or L(ine) should be ok.
Created issue #28 to track progress.
I also should mention that your implementation of PgUp and PgDown is quite slow when running on original speed because it has to animate the scroll many times. Maybe, if you going to implement an arbitrary line jump, maybe it's better to make the page change operation based on the same routine as well?
Tried that before, it was even slower :( The E: handler in the OS is very slow. Try loading an E: accelerator before the IDE, you can use my own https://github.com/dmsc/e-accelerator
Try attached ATR, type "EFAST" before loafing FB or FBI.
Copy and paste is difficult, as I don't currently have a buffer to store copied text, only a buffer for current line. What I could do is a key that remembers a line and then another that inserts a copy of that line in the current position, to do that I could store the memory address of an edited line. CONTROL-M (mark) and CONTROL-C (copy) ?
I could suggest another way. If you can dedicate a buffer for a single line, it means that copy-paste operations can be pipelined in a loop. If so, there can be two markers, start line and the end line which can be set by Ctrl-, and Ctrl-. (discussable) and operations like Ctrl-M(ove) and Ctrl-C(opy). It will take some extra code to detect the direction of movement for correctly modify the line numbers for movement and copying forward and backward but in exchange this could end up in nearly the only existing programming IDE for Atari with multi-line copy-paste and cut-paste operation.
Problem with the markers is that they would be invalidated on each edit operation, so I would need to keep them updated. With two markers, this is double the code for each operation.
I was thinking more on only one marker, but each copy/move operation advances the marker by one line. This way, to copy 5 lines from line 10 to line 100, you do: "CTRL-G 10 CTRL-M CTRL-G 100 CTRL-C CTRL-C CTRL-C CTRL-C CTRL-C", and if instead of CTRL-C you press CTRL-M, you move the lines.
Created issue #29 to track progress.
That is not as easy as it seems because the program is written without fixed memory locations - at compile time the linker decides where to put each piece of code.
Well, since almost any computer system provides some registers for user-defined use, the IDE could set it's own special value to one of the following memory locations which are described in "Mapping the Atari" book:
Note that to implement this, both the IDE and the standalone programs needs to write the location (with different values), so that the value is consistent.
What do you think on adding BREAK key support? See issue #10
There is a LOCATE command in the current version! It is used the same as the Atari BASIC one.
Oops, but this command isn't listed in the manual! Is there some updated manual version or a file where I can see the full list of implemented commands and operators?
The last version of the manual is always here, https://github.com/dmsc/fastbasic/blob/master/manual.md . I forgot to add LOCATE to the manual in the last version but added it on march 4.
The same is with locations marked by @ character. I saw lot of them in your BAS code and it looks like named defines but I haven't find any information on using that character and the list of built-in symbols (I mean named locations).
Those are specific to the cross-compiler, so are not in the manual but in the compiler usage text: https://github.com/dmsc/fastbasic/blob/master/compiler/USAGE.md#linking-other-assembly-files
As the compiled code is passed to the CA65 assembler, the named locations are really the ones included from the CA65 headers: https://github.com/cc65/cc65/blob/master/asminc/atari.inc , https://github.com/cc65/cc65/blob/master/asminc/atari_gtia.inc , https://github.com/cc65/cc65/blob/master/asminc/atari_antic.inc , https://github.com/cc65/cc65/blob/master/asminc/atari_pokey.inc
to be as fast as possible there is a custom fast PLOT routine for each Atari graphics mode, and the code is about 1kB.
Great, where can I read more about it?
Well, I learned a lot about TurboBasic XL when I was a child and hand-disassembled a lot of the code :)
You can browse my curren disassembly at https://github.com/dmsc/turbo-dis , the PAINT code starts at https://github.com/dmsc/turbo-dis/blob/master/turbo-mads.asm#L9751 but also uses the code for fast plot at https://github.com/dmsc/turbo-dis/blob/master/turbo-mads.asm#L9635 . Overall, it is about 300 lines of assembly code.
I don't want to add stack operated calls, but you can call procedures recursively (and the cross compiler even optimizes tail-calls to jumps).
Oh yes, I really noticed that because what I was exactly doing is implementing the closed loop fill function. But really with the need of maintaining the stack pointer manually, it's much easier to make a serious logical error.
I plan to implement PROCedures with parameters, to simplify code with a lot of procedures, but functions are really difficult because the way the compiler generates code.
Currently I code emulated behaviour of functions this way (which probably is similar to what as I can see in your IDE source code):
tempA=1:tempB=2:retvalue=0:exec routine
if retvalue
exec routine2
else
exec routine3
endif
'
proc routine ' example
if tempA=tempB
retvalue=1 ' return value assignment 1
endif
' else: return value remains at its default state
endproc
That would be possible with my "procedures with parameters", and I could add an extra parameter to procedures to store a return value into a variable passed as reference, so you can return values that way.
Probably, some function defining syntax can be convertible to a form similar to one described above. I see advantage of this approach in possibility to include generation of local variables, although this may be dangerous without storing them in a stack and just leaving at some arbitrary temporary memory locations.
Yes, functions are problematic because you have side-effects, using a proper stack is the only safe way to maintain all state. But, even if FastBasic uses a stack, it compiles code for a register machine.
This is because I discovered that using registers is faster in the interpreter, as the 6502 don't have good support for stack based operations. So, the VM has two registers: "AX" (the 16 bit accumulator) and "SADDR" (the 16 bit store-address register).
Then, many simple operations (like "A=2", or "POKE 123, 45" don't use the stack at all, the first is transformed to "LOAD_BYTE 2 ; STORE_VAR A", the second to "LOAD_WORD 123; STORE_SADDR ; LOAD_BYTE 45 ; STORE_BYTE_SADDR"
I was thinking more on only one marker, but each copy/move operation advances the marker by one line. This way, to copy 5 lines from line 10 to line 100, you do: "CTRL-G 10 CTRL-M CTRL-G 100 CTRL-C CTRL-C CTRL-C CTRL-C CTRL-C", and if instead of CTRL-C you press CTRL-M, you move the lines.
I think this gonna work. The only thing I feel doubted about is double assignment for Ctrl-M. In this routine I don't understand how to unmark a line or mark another for further copy/move operation. If Ctrl-M is reserved for marking, maybe it's better to assign Ctrl-C for copy and, say, Ctrl-X for movement?
What do you think on adding BREAK key support? See issue #10
I think, as long as it doesn't need any additional code to be included in the application, the way with the Break key is definitely better.
Hi!
I was thinking more on only one marker, but each copy/move operation advances the marker by one line. This way, to copy 5 lines from line 10 to line 100, you do: "CTRL-G 10 CTRL-M CTRL-G 100 CTRL-C CTRL-C CTRL-C CTRL-C CTRL-C", and if instead of CTRL-C you press CTRL-M, you move the lines.
I think this gonna work. The only thing I feel doubted about is double assignment for Ctrl-M. In this routine I don't understand how to unmark a line or mark another for further copy/move operation. If Ctrl-M is reserved for marking, maybe it's better to assign Ctrl-C for copy and, say, Ctrl-X for movement?
Just nonsense on my part, I meant CTRL-K form marking, or as you say CTRL-X for move, but in any case different keys!.
What do you think on adding BREAK key support? See issue #10
I think, as long as it doesn't need any additional code to be included in the application, the way with the Break key is definitely better.
Yes, I did a few tests by hooking on the break interrupt vector. Problem is, if you press break in the middle of an OS routine (like LOAD or SAVE from disk) the hardware state would be messed up.
Hi @rpocc !
Most of the IDE features requested are now implemented in the upcoming v4.4:
It would be great if you could test all the above.
Have Fun!
Thank you for developing this great IDE! Your totally modern and alternative approach to BASIC really reinvents the whole idea of high-level programming for 8-bit. It doesn't have so much optimized commands as Turbo BASIC but anyway, the numberless coding for 8-bit is a fantastic improvement. I keep my eye close on this project and would like to express my big respect!
I've tried to make a complete application with it and find several things, which I miss during development.
And by the way, is there any chance that some time in the future you will implement stack-operated function calls? Currently I've found a way for processing recursive code, using global temporary variables for data interchange between procedures and some array-emulated stack, but it's so complicated and so slow that becomes impractical. Meanwhile, since we are playing by rules of not using line numbers, labels and non-conditional jumps, implementing stack and functions could be super-helpful for keeping the code structured and readable. Sometimes I still miss labels and good old GOTO. Thanks.