News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_zhulien

Locomotive Shell 1.0 - Extending & Modifying Locomotive BASIC

Started by zhulien, 19:11, 24 January 22

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

zhulien

new_commands.asm for new LUT lookups
newcommand_DIR: jp command_CAT


; command_GRAPHICS_PAPER__GRAPHICS_PEN_and_set_graphics_draw_mode:;{{Addr=$c59a Code Calls/jump count: 0 Data use count: 1}}
        ; cp      $ba              ;{{c59a:feba}}  token for "PAPER"
        ; jr      z,eval_and_set_graphics_paper;{{c59c:2813}}  set graphics paper
     
        ; call    next_token_if_equals_inline_data_byte;{{c59e:cd25de}}
        ; defb $bb                  ; token for "PEN"
        ; cp      $2c              ;{{c5a2:fe2c}}  ','
        ; call    nz,validate_and_set_graphics_pen;{{c5a4:c4bac5}}  set graphics pen


newcommand_MCP:
;ret nz
push af
push bc
push de
push hl

push af
ld a, 0
ld ($be80), a
pop af

        ;cp $0d                  ;CR
;jr z, newcommand_MCPOnly

        cp $00                  ;zero
jr z, newcommand_MCPOnly

; check overloaded tokens that are not extensions before we read the next token
        cp $a7                  ;extension token to test "LIST"
jr z, newcommand_MCPLIST

; do we have extensions?
        cp $e9                  ;extension indicator token
jr nz, newcommand_MCPEnd

; read the next token
call get_next_token_skipping_space

        cp $80                  ;extension token to test "STATUS"
jr z, newcommand_MCPSTATUS

newcommand_MCPEnd:
ld ($be80), a
pop hl
pop de
pop bc
pop af
;jr newcommand_MCPOnly
ret

newcommand_MCPOnly:
;ld a, 1
ld ($be80), a
ld hl, mcp_message
call output_ASCIIZ_string

pop hl
pop de
pop bc
pop af
        ret

newcommand_MCPLIST:
;ld a, 2
ld ($be80), a
ld hl, mcp_list_message
call output_ASCIIZ_string

pop hl
pop de
pop bc
pop af
;xor a
        ret

newcommand_MCPSTATUS:
;ld a, 3
ld ($be80), a
ld hl, mcp_status_message
call output_ASCIIZ_string

pop hl
pop de
pop bc
pop af
;xor a
        ret

mcp_message:
defb "MCP",10,13,0  ; base mcp

mcp_list_message:
defb "MCP LIST",10,13,0  ; base mcp and overloaded list

mcp_status_message:
defb "MCP STATUS",10,13,0  ; base mcp, but extended status

; commands made of more than 1 word seemed previously to always have two variants implemented
; for example: symbol after.  symbol is a command that checks for a subsequent after, but after is also a valid command on it's own.
; similar for graphics pen, graphics paper, line input
; ultimately we get dual use of the 2nd keyword if it makes sense
; note: the overloaded second keyword must have the same token as the original use keyword
;
; for example, below is status, but above is also mcp status separately
newcommand_STATUS:
;ret nz
push af
push bc
push de
push hl

ld a, 4
ld ($be80), a
ld hl, status_message
call output_ASCIIZ_string

pop hl
pop de
pop bc
pop af
;xor a
        ret

status_message:
defb "STATUS",10,13,0 

; does nothing as we already have a list command, however we are using this token for extended list commands
newcommand_LIST:
ret

;; get extension table for letter
;; A = initial letter of BASIC extension
get_extension_table_for_letter:
        push    hl
        sub    $41              ;{initial letter - 'A'
                                  ; number in range 0->27
        add    a,a              ; x2 (two bytes per table entry)
                                  ; A = offset into table

        ;add    a,(extension_table_per_letter) and $ff;table starts at Low byte of keyword table address
        ;ld      l,a
        ;adc    a,(extension_table_per_letter >> 8);high byte of keyword table address
        ;sub    l
        ;ld      h,a

push bc
ld b, 0
ld c, a
ld hl, extension_table_per_letter
add hl, bc
pop bc

        ld      e,(hl)            ;get address of keyword list from table
        inc    hl
        ld      d,(hl)
        pop    hl
        ret

extension_to_code_address_LUT:
                                 
defw newcommand_STATUS ;STATUS $80
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;
defw doNothing ;

extension_to_code_address_LUTEnd:

extension_table_per_letter:

        defw extension_table_A      ;
        defw extension_table_B      ;
        defw extension_table_C      ;
        defw extension_table_D      ;
        defw extension_table_E      ;
        defw extension_table_F      ;
        defw extension_table_G      ;
        defw extension_table_H      ;
        defw extension_table_I      ;
        defw extension_table_J      ;
        defw extension_table_K      ;
        defw extension_table_L      ;
        defw extension_table_M      ;
        defw extension_table_N      ;
        defw extension_table_O      ;
        defw extension_table_P      ;
        defw extension_table_Q      ;
        defw extension_table_R      ;
        defw extension_table_S      ; STATUS
        defw extension_table_T      ;
        defw extension_table_U      ;
        defw extension_table_V      ;
        defw extension_table_W      ;
        defw extension_table_X      ;
        defw extension_table_Y      ;
        defw extension_table_Z      ;

defb "EXTENSION_TABLE:"
;;======================================================================
;; Extension table
;; list of extension as text followed by extension byte (token?)
;; end of list signalled with a 0 byte
;;
;; - BASIC extension stored excluding initial letter

;;=extension table Z
extension_table_Z:
                                 
        defb 0                   

;;=extension table Y
extension_table_Y:
                                 
        defb 0                   

;;=extension table X
extension_table_X:
                                 
        defb 0                   

;;=extension table W
extension_table_W:
                                 
        defb 0                   

;;=extension table V
extension_table_V:
                                 
        defb 0                   

;;=extension table U
extension_table_U:
                                 
        defb 0                   

;;=extension table T
extension_table_T:
                                 
        defb 0                   

;;=extension table S
extension_table_S:
                                 
        defb "TATU","S"+$80,$80    ; STATUS
        defb 0                   

;;=extension table R
extension_table_R:
                                 
        defb 0                   

;;=extension table Q
extension_table_Q:
                                 
        defb 0                   

;;=extension table P
extension_table_P:
                                 
        defb 0                   

;;=extension table O
extension_table_O:
                                 
        defb 0                   

;;=extension table N
extension_table_N:
                                 
        defb 0                   

;;=extension table M
extension_table_M:
                                 
        defb 0                   

;;=extension table L
extension_table_L:
                                 
        defb 0                   

;;=extension table K
extension_table_K:
                                 
        defb 0                   

;;=extension table J
extension_table_J:
                                 
        defb 0                   

;;=extension table I
extension_table_I:
                                 
        defb 0                   

;;=extension table H
extension_table_H:
                                 
        defb 0                   

;;=extension table G
extension_table_G:
                                 
        defb 0                   

;;=extension table F
extension_table_F:
                                 
        defb 0                   

;;=extension table E
extension_table_E:
                                 
        defb 0                   

;;=extension table D
extension_table_D:
                                 
        defb 0                   

;;=extension table C
extension_table_C:
                                 
        defb 0                   

;;=extension table B
extension_table_B:
                                 
        defb 0                   

;;=extension table A
extension_table_A:
                                 
        defb 0                   

defb "EXTENSION_END:"


Bread80

I hadn't thought before about how extra tokens would affect the keyword lookups.

It looks like your creating a whole second set of keyword tables. But you'll need to know which keyword (ASCII) you have in order to know which table to look in, and you won't know which table to look in until you've done the lookup.

My suggestions would be to take the current lookup tables and add a second byte (after the existing token byte) to specify the table. The ASCII to token mapping would need to skip over (or return) the extra byte.

For token to keyword mapping you could retain the existing subroutine for original code to use, but updating it to check the second token byte and create a separate routine for the new table, which would be a near copy except for the second byte checking.

(Or you could do this in one routine which checks both bytes. That would depend on how easy it is to adapt the calling code, and the availability of registers both for the call and the code itself).

zhulien

Actually the keywords string together to form sentences, that how how it is by default, currently up to 3 words I think (on error goto blah).  what is intersting, is that each keyword can be functional by itself, or with 2, or with 3 etc and each keyword that is strung together takes up a token, the only way to get more tokens is to sacrifice existing or as I have done, create a new lookup of tokens triggered by an extension token.  To make matters worst, for expressions, they use the same token space as the non-expression tokens, effectively overloaded so we don't want to mess that up too.

To keep changes to a minimum, the extension token $e9, if present does go lookup a 2nd table for which we can have all new keywords or use existing tokens as extension, eg.

extension tokens:

MCP
STATUS

standard tokens:

LIST - lists a basic program usually

combinations:

LIST - as usual
MCP STATUS - extension token followed by another extension token
MCP LIST - list behaviour specific to the MCP, perhaps list tasks, using extension token followed by standard token

I didn't want to complicate the existing code too much, but even to introduce a new list or keywords, we need to take space from somewhere.

We can do that by either somehow splitting the existing 16kb ROM into 2 ROMS trying to make the code organised with a minimal number of jumps - I started looking at this and it is very very difficult because almost all the logic for keywords, errors, etc seems to be needed from everywhere.  The only way to do this with a bit of effort would be to put the actual implementations of the keywords into a 2nd ROM, but then we are swapping ROMs for every invokation - might be a bit slow (compared to usual)?

I thought it is easier with the extension lookup because, we can essentially move that to the 2nd ROM (duplicating only the 1st keyword lookup table in the 2nd ROM purely for lookups) if we want to continue stringing words together.

The most logical way to extend it would be to introduce keywords that string together related subsystems of commands.

like 

GRAPHICS PEN, GRAPHICS PAPER
which have 3 keywords involved: GRAPHICS, PEN, PAPER (note PEN & PAPER can be used by themselves too).

So, we could introduce new GRAPHICS commands, new AUDIO commands, new TASK (or MCP) commands, new RAM access commands etc.

Sadly, having to sacrifice existing keywords from the ROM to make the job easier to get any space to even pass control to a 2nd ROM for the 2nd lookup table, that is why I thought to create Locomotive Shell.  Already proven that we can launch different flavours of BASIC via their RSXs, |BASIC, or |SHELL or |BASICPLUS, and they all co-exist nicely and RSXs seem to work perfectly in all.  Just be sure to choose the default one you like to have in the upper half of the system ROM.

Bread80

So, if I understand you, certain keywords cause the system to switch to an alternate keyword table for the tokens/keywords which follow them. That's actually quite neat.

I was tempted to mention that any new keywords will cause any old code using that word as a variable name will fail, but using a separate table, presumably, means no such problems.

As for splitting the ROM in two, I have that in my future plans, but plenty of other projects to do first. I'll need to create some tools to parse the code and find links to see what to put in each ROM (or both), and then to replace any links between ROMs with the appropriate system call.

TotO

I can imagine that people using a shell instead of the BASIC to run programs expect to only replace the ROM 0.
"You make one mistake in your life and the internet will never let you live it down" (Keith Goodyer)

zhulien

moved... to RASM thread

zhulien

I was looking through the lower rom disassembly here:

http://cpctech.cpc-live.com/docs/os.asm

A few thoughts.  Quite a lot of space can be recovered if some functionality is removed. 

Does anyone still use a printer on cpc?

Does anyone with disc machines still use tapes?

Would there be many games that actually use firmware graphics and maths functions or are they almost exclusively used by BASIC?

Machine startup messages, any need to keep them all?

Then I was thinking can any of it be optimized for speed if other code was removed.

Of course if stuff required by BASIC is removed it needs to be somewhere, perhaps in a 2nd BASIC ROM, although it means a slower far call to get to the 2nd ROM. But the 2nd ROM gives a full 16k, and we ultimately can have a 3rd or 4th ROM.

What if CPM programs could just be run from AMSDOS on 128k machines like any other program? 

What if the following were all combined into the CPC OS ROM...

MOS (multitasking across banked RAM)
AMSDOS Software support
CPM Software support
Primal Software support
Multiple CPU support
Extra Memory support

A bit off topic, with extra memory, would programmers prefer 16mb support with 256 byte (minimum memory access then translation) or 24 bit addressing?   Or 4mb support only?

GUNHED

And the power of the Z80 can be nearly doubled (in quite some cases) if the Firmware does NOT use the 2nd register file of the Z80 any longer. They just get wasted for interrupt handling. So instead of saving some values continuously in A' and BC' just put them to the RAM.
- This will reduce the overall system speed by approximately 0,001%
- But this will speed up most of the routine quite a well degree, of course they will need to be overworked to use 2nd register file instead of RAM. A complete rewrite would be indeed more beneficial.

- All this just in case you don't want to create you own completely new OS -  ;) ;D :) :) :)
http://futureos.de --> Get the revolutionary FutureOS (Update: 2023.11.30)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> Get the RSX-ROM for LambdaSpeak :-) (Updated: 2021.12.26)

zhulien

Currently on CPC we get approxi 42kb of BASIC program space using the current memory map:

EXISTING:

+--------+ 0000
|LOWJB   |
|PROGRAM |
+--------+ 4000
|PROGRAM |
|PROGRAM |
+--------+ 8000
|VARIABLE|
|HIGHJB  |
+--------+ C000
|SCREEN  |
|        |
+--------+ FFFF

Possible new memory map:

+--------+ 0000
|LOWJB   |
|VARIABLE|
+--------++--------++--------++--------++--------+ 4000
|PROGRAM ||PROGRAM ||PROGRAM ||PROGRAM ||PROGRAM |
|PROGRAM ||PROGRAM ||PROGRAM ||PROGRAM ||PROGRAM |
+--------++--------++--------++--------++--------+ 8000
|STACK   |
|HIGHJB  |
+--------+ C000
|SCREEN  |
|        |
+--------+ FFFF


If the above possible new memory map was used, on a standard CPC6128 would give 80kb himem PLUS 16kb (-0100H) variable space.  And 4mb+ on a 4mb CPC (still with 16kb variable space for now).

Looking at the lower ROM disassembly, it seems that the main firmware handles the loading of the BASIC program, and not the BASIC interpreter itself.  Somewhere in or before MC_START_PROGRAM is called.  I am not quite sure where it get's the load address from.  Ultimately I didn't want to change the lower ROM, but I seem to need to change it to load BASIC programs to #4000 instead of #0100 - and of course, enhance the loader to split the BASIC into lots of 16kb banks sideways during the load.  

Does anyone know where in the lower ROM this actually happens?  Or is it purely read from the load address stored in the header of the .BAS file vs .BIN files?

Why do this?  To cater for better software with more memory, I belive the memory map is better utilised sideways for single program (non-multitasking) - I have failed to convince everyone so far here though.  

I have proven however, 2 ways to expand functionality of BASIC - that is through the use of more keywords in BASIC, as well as through UniDOS Accessory ROMS.  Both have their own benefits.  UniDOS is unconventional for CPC way of doing things but does not take up any additional RSXs therefore not reducing HIMEM it seems regardless of how many BASIC enhancements implemented - this is more using a Linux way of doing things using virtual files to do stuff.

andycadley

Interesting ideas. As far as Sinclair BASIC goes, it shares a lot of keywords with Locomotive BASIC but the specific syntax and meaning is very different, I'm not sure automatic translation would necessarily work without a lot of effort.

If I were going to do something like this (and I considered it once upon a time) I'd be inclined to just use 24 bit pointers for everything (variables, code lines etc) and have the BASIC interpreter manage swapping the memory around as needed (including things like allowing PEEK and POKE to address all the RAM) - the Sam Coupé does that and it works really well. It is slower, but BASIC is interpreted anyway and the time impact is marginal. Space considerations might be more of an issue, but I'd assume it wouldn't be used on machines with less than 128K anyway.

McArti0

Basic has CHAIN and CHAIN MERGE ,DELETE. 
This is a natural method of changing the Basic code and accumulating it to a size larger than 42kb.
CPC 6128, Whole 6128 and Only 6128, with .....
NewPAL v3 for use all 128kB RAM by CRTC as VRAM
TYPICAL :) TV Funai 22FL532/10 with VGA-RGB-in.

andycadley

Quote from: McArti0 on 17:48, 11 September 23Basic has CHAIN and CHAIN MERGE ,DELETE.
This is a natural method of changing the Basic code and accumulating it to a size larger than 42kb.

For code, sure. But there are advantages (to a point) to being able to store larger arrays etc. Up to about 512K is probably reasonable to do in that fashion, much more than that and the structures needed to manage things start getting incompatible with running on 128K.

zhulien

For marge data sets like arrays i would create linked list commands that are in the central ram after the basic program, this bit is trivial. I have already coded RSXs linked list commands that support 512kb ram.  Alyssa Database just used extra ram like fixed size array elements which suited that application but isn't very flexible. 

Bread80

I'll point out one potential problem with storing BASIC code in sideways RAM: Where a variable stores a string constant the variable  (string pointer) will reference the string stored in the BASIC code (which saves memory). Thus the line:
s$="Hello!"
the Hello! is not copied to the strings area. This would create problems if the variable is referenced whilst that bank of RAM is paged out.

As for LOMEM and HIMEM, these are loaded into registers and passed to the foreground ROM. Consult the firmware manual. MC_START_PROGRAM should be the appropriate routine.

The start address of BASIC is part of the specification for foreground ROMs. It's the reason you can install other foreground ROM(s) in place of BASIC.

If you don't want to modify the firmware ROM, it might be possible to hack this via the RSX/ROM initialisation procedure. I.e. when your ROM gets initialised set the registers appropriately and call MC_START_PROGRAM to restart BASIC. You'll obviously need to detect the second call. Which may not be easy as memory gets cleared.

Having said that, IIRC, the ROM initialisation procedure allows you to modify LOMEM and HIMEM. If that's correct this will be easy :-)

BTW the RSX/ROM initialisation is called by BASIC at startup.

Bread80

BTW if you ask me it would be cooler to be able to store multiple BASIC programs in memory and switch between them. The Elan Enterprise had built in support for that.

Realistically that would limit you to 16k code and data. And you'd need a way to preserve the BASIC data area between programs. Maybe using the spare non-banked memory?

zhulien

Actually it may be possible to have 
Quote from: Bread80 on 23:28, 25 September 23BTW if you ask me it would be cooler to be able to store multiple BASIC programs in memory and switch between them. The Elan Enterprise had built in support for that.

Realistically that would limit you to 16k code and data. And you'd need a way to preserve the BASIC data area between programs. Maybe using the spare non-banked memory?
Entire 64k chunks swapped around, it seems if I manually swap memory around in basic, basic is still working... I just cannot see it, but I can type and press del to make it beep.

My MOS kernel originally was going to allow multiple shells but I think its better as drivers for basic.  Just I failed to convince anyone of conforming to any driver standard previously. Nobody has any interest it seems in standardization on cpc. 

zhulien

Quote from: Bread80 on 12:19, 21 February 22I'd suggest a lot of the changes your making are more a firmware thing rather than a BASIC thing. If you put them in the firmware then there available for any software to use. And it's a lot easier to strip out space in the firmware - the floating point maths stuff can move to a sideways ROM. And the printer stuff sounds like a safe bet to get rid of. Maybe even the cassette routines depending on how you like to load and save stuff.
I think this might be a better idea - i will investigate this too

GUNHED

http://futureos.de --> Get the revolutionary FutureOS (Update: 2023.11.30)
http://futureos.cpc-live.com/files/LambdaSpeak_RSX_by_TFM.zip --> Get the RSX-ROM for LambdaSpeak :-) (Updated: 2021.12.26)

zhulien

changes made by zhulien:

- removed hardcoded 128k from startup message
- added dynamic memory check (placeholder for now)
- shortened the startup error message to gain some space
- removed all the brand names and name lookup in favour of a hardcoded Amstrad
- removed 60hz support as this is seldom used
- moved graphics, text and screen operations (including FONT) to ROM 31 so that it could be considered a video driver extension (perhaps this could be made to work like OffseT's UniDOS extension ROMs to support different graphics cards down the track)

todo:

- modify the huge FAR jumpblock to be calculated (perhaps) to take less bytes, need to see if it lowers performance too greatly though

issues:

- a couple of functions relating to RAMLAM LDIR and RAMLAM CLEAR should be recoded to be faster
- inks are not correct and colour cycling event is not working
- some badly coded software doesn't work

ROM sizes:

lower: 10894 bytes, 5490 bytes claimed
upper: 6589 bytes , 9795 bytes left

how does it work:

2 ROMS, the moved functions are now in ROM 31 and are accessed by the lower ROM via a jumpblock of RES 3s.  The upper ROM uses the normal jumpblock
where necessary (only a few functions) to call the lower ROM. A couple of functions in the upper ROM are slow due to using RAMLAM but they could be
improved either to use a buffer at BE80 or in extra RAM. The RES 3 jumpblock in the lower ROM could be shrunk to be calculated - but will that be
faster or slower? note sure.

next:

- Examine the best ways to extend functionality in the lower ROM, perhaps adding new commands? or perhaps jumpblock entries in the ROM 31 could
be expanded... I still think that a mixture of this experment and the UniDOS streamer ROM can work well together also - streamer could use this newer
ROM. 

- API extensions that could benefit.  Memory management! So d*head Jason can't get his way...
- maybe to make it more compatible with badly written software, i will need to put as many entry points back in place as possible - and work within the gaps.

zhulien

You can build them with RASM, or put them in WinAPE (newlower.rom goes in ROM 0, newupper.rom goes in ROM 31).

zhulien

Here is another build with source with most of the original entry points in-tact (I hope). Of course the font is not in the ROM.

I need to see if RASM will produce a symbol table and I need to verify it.

I still haven't figured out why the inks don't work.

XeNoMoRPH



Ok, and now what can I do, how can I test this?
your amstrad news source in spanish language : https://auamstrad.es

zhulien

At the moment it is to test and see how compatible it is with software. I really do believe in the 2nd build there i must have some of the entry points wrong.

I was surprised that the following work:

Discology 6 rom
Turbo esprit by domain 
Unidos
Orgams (mostly)
Basic
Alyssa database with fast text patch removed 

What didn't work for me yet

Cpm 2.2
Cpm plus
My old fast text patch for mode 2
Protext rom (corrupted fonts)
Maxam 1.5 rom (invisible text sometimes)
Alien (game, I can hear it but the inks are broken)

I don't really understand how inks work at the hardware level on cpc.  It looks in the code to do the following.  There is an interrupt that cycles through 2 sets of inks, it does this always even if they are 2 sets of the same inks.  But events can't work currently in the 2nd rom so I changed them to be in the first rom.  But oddly they don't show right colours often making all text invisible.

If I verify the symbol table (if that is the cause of some crashes) and fix the inks and prove its compatible then... we have room in the roms for enhancements such as history in the editor perhaps, as mentioned, memory management will be a priority for me.  

Also before I was looking at adding commands to locomotive basic but the issue it is a very full rom, and it is hardntonsplit into 2, however... its much easier to put some of the basic logic in the lower rom that is visible at the same time as basic, than to move it to another sideways rom.

Powered by SMFPacks Menu Editor Mod