1

Hello,

TeoT Alpha has just been released for the neogeo smile



Also introducing ngDataLinker a little tool and method to use bank switching using NEODEV :
https://github.com/ozzyyzzo4096/ngDataLinker

Enjoy smile
avatar

2

All I can say is wow! The Alpha looks terrific! You can tell you guys put some real love into it!!

It is going to be a terrific week!

Thanks soooo much for this great tool!
I'm really curious to see if I can get it working!

This is really exciting stuff!!! Thanks again!!!

3

Thanks ^^

Yep we are putting our best efforts and love into this project , next version (closed Beta) will bring better controls + tuning and at a later date the final rom will be released for free smile

Concerning bank switching it just works as shown with this Alpha (proof of concept) ;-)

For your own projects you just need several makefiles where you put your data (tables/obj) in.

NgDataLinker will take care of the addresses , everything is explained on the git Readme ;-)

For testing out, i recommend to use Mame as there are compatibilities issues with P roms layout using NeoSD and NeoBuilderUI_1.06 which is expecting *only* two roms P1 (1MB) and P2 (1 to 8MB) .

To get the proper layout and build up P2 from several files u can use the new version of RomWak 0.3f as i've added to it a new concatenate option /c

https://github.com/freem/romwak/


Have fun smile
avatar

4

Really impressive work love
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

5

Really very impressive but there is still a lot of work to do... I hope you can do it, then it could be a awesome project! smile

6

I will apologize in advance as I will probably butcher this...

I'm really excited to see if I can get 8x time more P memory built into my current coding rig!!!!!!!!!!!
My goal is to see if based on what I am describing the NgDataLinker 1.0 can fit into my setup?

To my knowledge I am using Banking via DATLib at the moment.

I can access 4 banks of GFX data but my limitation is I cannot combine gfx from these banks in the same scene.
I also believe 8x more P means the ability to build more GFX!!!!!!! rotfl

Ok I am going to try and describe how it is all setup now.
It has been a very long time since I have reviewed this stuff and I am not great at the technical stuff.

I have 4 .xml files that build the GFX data. Each file has a setup at the top of it.
"fillmode" changes in each file.
I think each Bank is set to be 8mb's in size

EX:

//Bank 0 <setup> <starting_tile fillmode="dummy">256</starting_tile> <charfile>out\bk0Char.bin</charfile> <mapfile>out\bk0Maps.s</mapfile> <palfile>out\bk0Pals.s</palfile> <incfile>out\bk0Include.h</incfile> </setup> //Bank 1 <setup> <starting_tile fillmode="none">65536</starting_tile> <charfile>out\bk1Char.bin</charfile> <mapfile>out\bk1Maps.s</mapfile> <palfile>out\bk1Pals.s</palfile> <incfile>out\bk1Include.h</incfile> </setup> //Bank2 <setup> <starting_tile fillmode="none">131072</starting_tile> <charfile>out\bk2Char.bin</charfile> <mapfile>out\bk2Maps.s</mapfile> <palfile>out\bk2Pals.s</palfile> <incfile>out\bk2Include.h</incfile> </setup> //Bank 3 <setup> <starting_tile fillmode="none">196608</starting_tile> <charfile>out\bk3Char.bin</charfile> <mapfile>out\bk3Maps.s</mapfile> <palfile>out\bk3Pals.s</palfile> <incfile>out\bk3Include.h</incfile> </setup>
Then I get Bank0.h Bank1.h Bank2.h Bank3.h that I include in my project.

Here is an example of Bank0.h all the other Bank files are very similar.
*Edit - I think DATLib is doing what the NgDataLinker 1.0 is doing when generating the Bank0-3.h files in this step. (but I could be wrong)

#ifndef __BANK0GFX_H__ #define __BANK0GFX_H__ #define BoxOrange 0x0020458e #define Mission1 0x00200160 #define GD_Explosion_Palettes 0x002441fe #define BoxBrown 0x00204624 #define BoxPurple_Palettes 0x00243e8c #define BoxYellow 0x002045ac #define Bios_Palettes 0x00243dc0 #define GD_Explosion 0x00242f46 #define BoxPink 0x00204642 #define Bullet1_Palettes 0x002441bc #define TM_00177 0x0021d80a #define Bullet1 0x002425c8 #define BoxRed 0x00204660 #define Bios 0x00200000 #define BoxOrange_Palettes 0x00243e26 #define Rob 0x0023732a #define BoxGreen_Palettes 0x00243e04 #define BoxYellow_Palettes 0x00243e48 #define DebugBox0 0x0020467e #define Rob_Palettes 0x00243f9a #define BoxBlue_Palettes 0x00243e6a #define BoxTeal_Palettes 0x00243eae #define BoxRed_Palettes 0x00243f14 #define BoxGreen 0x00204570 #define BoxPink_Palettes 0x00243ef2 #define BoxBlue 0x002045ca #define TM_00177_Palettes 0x00243f58 #define BoxBrown_Palettes 0x00243ed0 #define BoxPurple 0x002045e8 #define BoxTeal 0x00204606 #define Mission1_Palettes 0x00243de2 #define DebugBox0_Palettes 0x00243f36 #endif
To access my the Banks via code I use this command then I call the GFX I want to display.

//Bank 0 volMEMWORD(0x2ffff0)=0; //Display GFX //Bank 1 volMEMWORD(0x2ffff0)=1; //Display GFX //Bank 2 volMEMWORD(0x2ffff0)=2; //Display GFX //Bank 3 volMEMWORD(0x2ffff0)=3; //Display GFX
The makefile I use is never altered it looks like this.
(I do all my coding in .h files...I realize it is probably bad form but it helps me be versatile and jump to other projects without changing the makefile)

# $Id: Makefile,v 1.4 2001/05/03 13:43:42 fma Exp $ ####################################### # Base dir of your m68k gcc toolchain # ####################################### BASEDIR = $(NEODEV) AS = as LD = gcc CC = gcc AR = ar OBJC = objcopy BIN2O = bin2elf GFXCC = gfxcc FIXCNV = fixcnv ####################################### # Path to libraries and include files # ####################################### INCDIR = $(BASEDIR)/m68k/include LIBDIR = $(BASEDIR)/m68k/lib TMPDIR = tmp ################################### # Output: {cart, cd} *lower case* # ################################### OUTPUT = cart #OUTPUT = cd ############################ # Settings for cart output # ############################ ROMSIZE = 0x20000 PADBYTE = 0xFF ############################## # Object Files and Libraries # ############################## OBJS = $(TMPDIR)/crt0_$(OUTPUT).o $(TMPDIR)/main.o LIBS = -lDATlib -lprocess -lc -lgcc ##################### # Compilation Flags # ##################### ASFLAGS = -m68000 --register-prefix-optional LDFLAGS = -Wl,-T$(BASEDIR)/src/system/neo$(OUTPUT).x -Xlinker -Map=output.map CCFLAGS = -m68000 -O3 -Wall -fomit-frame-pointer -ffast-math -fno-builtin -nostartfiles -nodefaultlibs -D__$(OUTPUT)__ ARFLAGS = cr DEBUG = -g ################## # FIX Definition # ################## FIXFILES = $(BASEDIR)/src/shared/fix_font.bmp ############## # Make rules # ############## ifeq ($(OUTPUT),cart) Build/dev_p1.bin : Build/test.o # $(OBJC) --gap-fill=$(PADBYTE) --pad-to=$(ROMSIZE) -R .data -O binary $< $@ $(OBJC) --gap-fill=$(PADBYTE) -R .data -O binary $< $@ # copy dev_p1.rom c:\mame\roms\dev.bin /y else Build/test.prg : Build/test.o $(OBJC) -O binary $< $@ endif #test.o : test.fix $(OBJS) Build/test.o : $(OBJS) $(LD) -L$(LIBDIR) $(CCFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ $(TMPDIR)/%.o: %.c $(CC) -I$(INCDIR) $(CCFLAGS) -c $< -o $@ $(TMPDIR)/%.o: %.s $(AS) $(ASFLAGS) $< -o $@ clean: rm -f $(TMPDIR)/*.* rm -f palettes.pal rm -f test.o rm -f test.prg rm -f test.fix rm -f test.spr rm -f dev_p1.rom ############ # FIX Rule # ############ #test.fix : $(FIXFILES) # $(FIXCNV) $(FIXFILES) -o $@ -pal palettes.pal # #$(TMPDIR)/palettes.o: palettes.pal # $(BIN2O) $< palettes $@
Any tips or suggestions to on if it is possible to build this into the existing rig would be amazing. Thanks again for this great enhancement!!!!!

7

blastar (./5):
Really very impressive but there is still a lot of work to do... I hope you can do it, then it could be a awesome project! smile

yep, a lot of work needs to be done you're definitely right !!

Luckily, we had super cool help from some experienced people with a recent NeoSoundBuiler update and it's new crazy amount of 1792 sfx and the help to fix up Neo-SD compat issues was really welcome. ;-)
avatar

8

Mega Shocked (./6):
I will apologize in advance as I will probably butcher this...


Any tips or suggestions to on if it is possible to build this into the existing rig would be amazing. Thanks again for this great enhancement!!!!!


I'm not using DATlib myself, and i'm not aware of its limitations if any, but i guess it should be possible to do so.
*Unfortnately your makefile listing is not complete*

But anyhow, what you will need is to add more makefiles to your build process.

atm, your main makefile is generating dev_p1.rom and should contains all your OBJS (.o)
as suggested by the output name, everything will be located into P1 ROM (1MB)

just create another makefile which will output dev_p2.rom and move some OBJS from the original makefile in this one (by this you are intentionally associating data from P1 to P2)
then use romwak accordingly on dev_p2.rom (just like it is done with dev_p1.rom)

Finally, if you need more than 1MB in P2 you will need to concatenate multiple files (ex : xxx-p2..rom+xxx-p3.rom+xxx-p4.rom = xxx-p2.bin)

For TeoT for instance i'm building 9 makeifles generating : dev_p1.rom to dev_p9.rom
then using romwak, building roms process is as follow :

romwak /f dev_p1.rom 202-p1.bin
romwak /p 202-p1.bin 202-p1.bin 1024 255

romwak /f dev_p2.rom 202-p2.bin
romwak /p 202-p2.bin 202-p2.bin 1024 255

romwak /f dev_p3.rom 202-p3.rom
romwak /p 202-p3.rom 202-p3.rom 1024 255
romwak /c 202-p2.bin 202-p3.rom 202-p2.bin

romwak /f dev_p4.rom 202-p4.rom
romwak /p 202-p4.rom 202-p4.rom 1024 255
romwak /c 202-p2.bin 202-p4.rom 202-p2.bin

romwak /f dev_p5.rom 202-p5.rom
romwak /p 202-p5.rom 202-p5.rom 1024 255
romwak /c 202-p2.bin 202-p5.rom 202-p2.bin

romwak /f dev_p6.rom 202-p6.rom
romwak /p 202-p6.rom 202-p6.rom 1024 255
romwak /c 202-p2.bin 202-p6.rom 202-p2.bin

romwak /f dev_p7.rom 202-p7.rom
romwak /p 202-p7.rom 202-p7.rom 1024 255
romwak /c 202-p2.bin 202-p7.rom 202-p2.bin

romwak /f dev_p8.rom 202-p8.rom
romwak /p 202-p8.rom 202-p8.rom 1024 255
romwak /c 202-p2.bin 202-p8.rom 202-p2.bin

romwak /f dev_p9.rom 202-p9.rom
romwak /p 202-p9.rom 202-p9.rom 1024 255
romwak /c 202-p2.bin 202-p9.rom 202-p2.bin

hope that helps smile

note: you will have to build by yourself the new version of romwak (0.3f) which contains the concatenate option /c
(freem seems lazy enough not providing the latest binary release) lol ... just kidding... ^^

* update *

It's kinda obvious but using ngDataLinker with P1 (map file) is irrelevant as data from P1 must stay into the memory range [0x00100000- [0x001fffff] as generated by GCC link.
it has to be used with P2 roms and above to keep addresses between range [0x00200000- [0x002fffff] where bank switch mecanism occurs and data can be accessed.

* hint *

Just try with a simple configuration at first : 2 makefiles , let's also forget sprites and others generated data from third party applications.

- don't change anything to your original build setup and let's call your original makefile as 'P1'

- create a second makefile which will output dev_p2.rom and add whatever object you will need to access from your code.. a table for instance.

you could compile and link a C file from the second makefile containing :

const char const messageInABottle[] = "I'll send an S.O.S to the world";

you also have to access the data from somewhere in your OWN code in P1 like :

sprintf(txt,"%s, I hope that someone gets my..",messageInABottle);

but.. compiling and linking P1 will fail because there is no reference about messageInABottle table into modules compiled from P1 makefile.

- use ngDataLinker with P2 map file

generated header should contain a crazy immediate definition like this : #define messageInABottle 0x00200010

- #include the generated header into the P1 source file where messageInABottle table has to be accessed.

- recompile and link P1

- success and you are now accessing a string from P2 , also note that there is no need to specifiy any bank @0x2ffff0 because 1MB P1 and 1MB P2 is the default configuration.


'Message in a bottle, yeah
A year has passed since I wrote my note
I should have known this right from the start'
The Police
avatar

9

Thank you for seeing my SOS and reviewing my message in a bottle.

I tried your suggestion and of creating a P2 rom with some data in it.
Upon doing this I was met with challenges and revelations.

1. In my working directory I created a folder called P2 in that folder another folder titled P2_1
P2>P2_1
etc P2_2 (later on)

The folder contains:

common_crt0_cart.s
crt0_cart.s
LevelCollisionMap.c (Data File I am attempting to include in the P2 makefile)
tmp (directory)
build (directory)
makefile

Sad makefile so far.
# $Id: Makefile,v 1.4 2001/05/03 13:43:42 fma Exp $ ####################################### # Base dir of your m68k gcc toolchain # ####################################### BASEDIR = $(NEODEV) AS = as LD = gcc CC = gcc AR = ar OBJC = objcopy BIN2O = bin2elf GFXCC = gfxcc FIXCNV = fixcnv ####################################### # Path to libraries and include files # ####################################### INCDIR = $(BASEDIR)/m68k/include LIBDIR = $(BASEDIR)/m68k/lib TMPDIR = tmp ################################### # Output: {cart, cd} *lower case* # ################################### OUTPUT = cart #OUTPUT = cd ############################ # Settings for cart output # ############################ ROMSIZE = 0x100000 PADBYTE = 0x00 ############################## # Object Files and Libraries # ############################## OBJS = $(TMPDIR)/crt0_$(OUTPUT).o $(TMPDIR)/LevelCollisionMap.o LIBS = -lDATlib -lprocess -lc -lgcc ##################### # Compilation Flags # ##################### ASFLAGS = -m68000 --register-prefix-optional #LDFLAGS = -Wl,-T$(BASEDIR)/src/system/neo$(OUTPUT).x -Xlinker -Map=output.map CCFLAGS = -m68000 -O3 -Wall -fomit-frame-pointer -ffast-math -fno-builtin -nostartfiles -nodefaultlibs -D__$(OUTPUT)__ ARFLAGS = cr DEBUG = -g ############## # Make rules # ############## Build/dev_p2.bin : Build/test.o $(OBJC) --gap-fill=$(PADBYTE) --pad-to=$(ROMSIZE) -R .data -O binary $< $@ #test.o : test.fix $(OBJS) Build/test.o : $(OBJS) $(LD) -L$(LIBDIR) $(CCFLAGS) $(OBJS) $(LIBS) -o $@ $(TMPDIR)/%.o: %.c $(CC) -I$(INCDIR) $(CCFLAGS) -c $< -o $@ $(TMPDIR)/%.o: %.s $(AS) $(ASFLAGS) $< -o $@ clean: rm -f $(TMPDIR)/*.* rm -f palettes.pal rm -f test.o rm -f test.prg rm -f test.fix rm -f test.spr rm -f dev_p2.rom
"common_crt0_cart.s" - this is an altered version of the NeoDev file provided by DATLib when I run the makefile there are errors related to this file.
I guess the big question is can I somehow use the makefile and ignore incorporating this file into the build process?

c:\NeoDev\src\samples\GAME\P2\P2_1>make gcc -Lc:\neodev/m68k/lib -m68000 -O3 -Wall -fomit-frame-pointer -ffast-math -fno-builtin -nostartfiles -nodefaultlibs -D__cart__ tmp/crt0_cart.o tmp/LevelCollisionMap.o -lDATlib -lprocess -lc -lgcc -o Build/test.o tmp/crt0_cart.o: In function `__security_code': tmp/crt0_cart.o(.text+0x10e): undefined reference to `bkp_data' tmp/crt0_cart.o: In function `_entry_user': tmp/crt0_cart.o(.text+0x1f4): undefined reference to `USER' tmp/crt0_cart.o: In function `_entry_player_start': tmp/crt0_cart.o(.text+0x1fa): undefined reference to `PLAYER_START' tmp/crt0_cart.o: In function `_entry_demo_end': tmp/crt0_cart.o(.text+0x202): undefined reference to `DEMO_END' tmp/crt0_cart.o: In function `_entry_coin_sound': tmp/crt0_cart.o(.text+0x20a): undefined reference to `COIN_SOUND'
common_crt0_cart.s

* ++====================================================================++ * || common_crt0_cart.s - C Run Time Startup Code for Neo Geo Cartridge || * ++--------------------------------------------------------------------++ * || $Id: common_crt0_cart.s,v 1.5 2001/07/13 14:46:31 fma Exp $ || * ++--------------------------------------------------------------------++ * || This is the startup code needed by programs compiled with GCC || * ++--------------------------------------------------------------------|| * || BGM: Guitar Vader - S.P.Y. || * ++====================================================================++ _ZERO_DIVIDE = 0x00c00426 _CHK_CMD = 0x00c00426 _TRAPV_CMD = 0x00c00426 _NPC_1010 = 0x00c00426 _NPC_1111 = 0x00c00426 _IRQ4 = 0x00c00426 _IRQ5 = 0x00c00426 _IRQ6 = 0x00c00426 _IRQ7 = 0x00c00426 ********************** Exported Symbols ********************** .globl _start .globl atexit ********************** Imported Symbols ********************** .globl __do_global_ctors .globl __do_global_dtors .globl main .globl memset .globl __bss_start .globl _end ********************** Program Start ************************* ** NOTE: Cartridge systems have swapped IRQ1 and IRQ2 .org 0x0000 .long 0x0010f300 |;reset stack ptr .long 0x00c00402 /*;reset ptr*/ .long 0x00c00408 /*;bus error*/ .long 0x00c0040e /*;address error*/ .long 0x00c00414 /*;illegal instruction*/ .long _ZERO_DIVIDE /*;division by 0*/ .long _CHK_CMD /*;CHK command*/ .long _TRAPV_CMD /*;TRAPV command*/ .long 0x00c0041a /*;illegal privilege*/ .long 0x00c00420 /*;trace exception handling*/ .long _NPC_1010 /*;no package command (1010)*/ .long _NPC_1111 /*;no package command (1111)*/ .long 0x00c00426, 0x00c00426, 0x00c00426 /*;unused*/ .long 0x00c0042c /*;uninitialized interrupt*/ .long 0x00c00426, 0x00c00426, 0x00c00426, 0x00c00426 /*;unused*/ .long 0x00c00426, 0x00c00426, 0x00c00426, 0x00c00426 /*;unused*/ .long 0x00c00432 /*;virtual interrupt*/ |; 0x64 .long _IRQ2, _IRQ1, _IRQ3, _IRQ4, _IRQ5, _IRQ6, _IRQ7 /*;4-7 unused*/ __security_code: .long 0x76004a6d, 0x0a146600, 0x003c206d, 0x0a043e2d .long 0x0a0813c0, 0x00300001, 0x32100c01, 0x00ff671a .long 0x30280002, 0xb02d0ace, 0x66103028, 0x0004b02d .long 0x0acf6606, 0xb22d0ad0, 0x67085088, 0x51cfffd4 .long 0x36074e75, 0x206d0a04, 0x3e2d0a08, 0x3210e049 .long 0x0c0100ff, 0x671a3010, 0xb02d0ace, 0x66123028 .long 0x0002e048, 0xb02d0acf, 0x6606b22d, 0x0ad06708 .long 0x588851cf, 0xffd83607 .word 0x4e75 .org 0x100 .ascii "NEO-GEO\0" .word _NGH .long _PROGRAM_SIZE .long _WRK_BCKP_AREA .word _WRK_BCKP_AREA_SIZE .byte _EYE_CATCHER .byte _EYE_CATCHER_TILES .long JPConfig .long USConfig .long EUConfig jmp _ENTRY_USER jmp _ENTRY_PLAYER_START jmp _ENTRY_DEMO_END jmp _ENTRY_COIN_SOUND _irq3_handler: move.w #1, 0x3C000C _dummy_exc_handler: rte atexit: |;Dummy atexit (does nothing for now) moveq #0, d0 _dummy_config_handler: rts _irq1_handler: |;Standard IRQ1 handler move.w #2, 0x3C000C rte .org 0x182 .long __security_code _dummyTIdata: .word 0x0000 |;* Entry point of our program _start: |;* Setup stack pointer and 'system' pointer |;lea 0x10F300,a7 |;lea 0x108000,a5 |;* Reset watchdog move.b d0, 0x300001 |;* Flush interrupts move.b #7, 0x3C000C move.l #0, TInextTable |;* Enable interrupts move.w #0x2000,sr |;* Initialize BSS section move.l #_end, d0 sub.l #__bss_start, d0 move.l d0, -(a7) clr.l -(a7) pea __bss_start jbsr memset jsr 0xc004c2 |; FIX_CLEAR jsr 0xc004c8 |; LSP_1st |;* Jump to main |;* jbsr main |;* Call global destructors jbsr __do_global_dtors |;* For cart systems, infinite loop 9: jmp 9b(pc) _entry_user: |;* Setup stack pointer and 'system' pointer |;* stack is set up by bios |;* Reset watchdog move.b d0, 0x300001 |;* Flush interrupts move.b #7, 0x3C000C move.l #0, TInextTable |;* Enable interrupts move.w #0x2000,sr jmp USER _entry_player_start: jmp PLAYER_START rts _entry_demo_end: jmp DEMO_END rts _entry_coin_sound: jmp COIN_SOUND rts

2. DATLib ships with neocart.x and neocartBank.x

I realize I have only been using neocart.x in my P1 makefile I have never actually used neocartBank.x (What the heck!?)
It looks like neocartBank.x focuses on building the 1mb P2 rom (I could be wrong)

So all these years I have only been using a P1 rom to access my gfx banks I guess...??

I wish I could see an example of a makefile using neocartBank.x to see if I can somehow work it into my setup.

I commented out the inclusion of the .x file in P2 makefile to see if I could get it to build but no dice.

Here are those files listed as a reference:

neocart.x

/* $Id: neocart.x,v 1.2 2001/04/13 09:36:11 fma Exp $ */ OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k") OUTPUT_ARCH(m68k) ENTRY(_start) SEARCH_DIR(/usr/m68k/m68k-elf/lib); /* Do we need any of these for elf? __DYNAMIC = 0; */ MEMORY { /*roma (rx) : ORIGIN = 0x00000000, LENGTH = 0x100000 ram (rwx) : ORIGIN = 0x00100000, LENGTH = 0xE000 romb (rx) : ORIGIN = 0x00110000, LENGTH = 0xF0000 */ roma (rx) : ORIGIN = 0x00000000, LENGTH = 0x100000 ram (rwx) : ORIGIN = 0x00100000, LENGTH = 0xF000 romb (rx) : ORIGIN = 0x00200000, LENGTH = 0x100000 } SECTIONS { /* Read-only sections, merged into text segment: */ . = 0x80000000 + SIZEOF_HEADERS; .interp : { *(.interp) } .hash : { *(.hash) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } .gnu.version : { *(.gnu.version) } .gnu.version_d : { *(.gnu.version_d) } .gnu.version_r : { *(.gnu.version_r) } .rel.init : { *(.rel.init) } .rela.init : { *(.rela.init) } .rel.text : { *(.rel.text) *(.rel.text.*) *(.rel.gnu.linkonce.t*) } .rela.text : { *(.rela.text) *(.rela.text.*) *(.rela.gnu.linkonce.t*) } .rel.fini : { *(.rel.fini) } .rela.fini : { *(.rela.fini) } .rel.rodata : { *(.rel.rodata) *(.rel.rodata.*) *(.rel.gnu.linkonce.r*) } .rela.rodata : { *(.rela.rodata) *(.rela.rodata.*) *(.rela.gnu.linkonce.r*) } .rel.data : { *(.rel.data) *(.rel.data.*) *(.rel.gnu.linkonce.d*) } .rela.data : { *(.rela.data) *(.rela.data.*) *(.rela.gnu.linkonce.d*) } .rel.ctors : { *(.rel.ctors) } .rela.ctors : { *(.rela.ctors) } .rel.dtors : { *(.rel.dtors) } .rela.dtors : { *(.rela.dtors) } .rel.got : { *(.rel.got) } .rela.got : { *(.rela.got) } .rel.sdata : { *(.rel.sdata) *(.rel.sdata.*) *(.rel.gnu.linkonce.s*) } .rela.sdata : { *(.rela.sdata) *(.rela.sdata.*) *(.rela.gnu.linkonce.s*) } .rel.sbss : { *(.rel.sbss) } .rela.sbss : { *(.rela.sbss) } .rel.bss : { *(.rel.bss) } .rela.bss : { *(.rela.bss) } .rel.plt : { *(.rel.plt) } .rela.plt : { *(.rela.plt) } .init : { KEEP (*(.init)) } =0x4e75 .plt : { *(.plt) } .text : { *(.text) *(.text.*) *(.stub) /* .gnu.warning sections are handled specially by elf32.em. */ *(.gnu.warning) *(.gnu.linkonce.t*) . = ALIGN(0x4); __CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) *(.ctors) LONG(0) __CTOR_END__ = .; __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) *(.dtors) LONG(0) __DTOR_END__ = .; } =0x4e75 _etext = .; PROVIDE (etext = .); .fini : { KEEP (*(.fini)) } =0x4e75 .rodata : { *(.rodata) *(.rodata.*) *(.gnu.linkonce.r*) } .rodata1 : { *(.rodata1) } .gcc_except_table : { *(.gcc_except_table) } /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ .ctors ALIGN(4): { /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it doesn't matter if the user does not actually link against crtbegin.o; the linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o is in. */ KEEP (*crtbegin.o(.ctors)) /* We don't want to include the .ctor section from from the crtend.o file until after the sorted ctors. The .ctor section from the crtend file contains the end of ctors marker and it must be last */ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) } .dtors : { KEEP (*crtbegin.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) } . = ALIGN(0x10) + (. & (0x10 - 1)); .data : { *(.data) *(.data.*) *(.gnu.linkonce.d*) } .data1 : { *(.data1) } .eh_frame : { *(.eh_frame) } .got : { *(.got.plt) *(.got) } .dynamic : { *(.dynamic) } /* We want the small data sections together, so single-instruction offsets can access them all, and initialized data all before uninitialized, so we can shorten the on-disk segment size. */ .sdata : { *(.sdata) *(.sdata.*) *(.gnu.linkonce.s.*) } _edata = .; PROVIDE (edata = .); __bss_start = .; .sbss : { *(.dynsbss) *(.sbss) *(.sbss.*) *(.scommon) } .bss : { *(.dynbss) *(.bss) *(.bss.*) *(COMMON) /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the .bss section disappears because there are no input sections. */ . = ALIGN(32 / 8); } . = ALIGN(32 / 8); _end = .; PROVIDE (end = .); /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } /* These must appear regardless of . */ }

neocartBank.x

/* $Id: neocart.x,v 1.2 2001/04/13 09:36:11 fma Exp $ */ OUTPUT_FORMAT("elf32-m68k", "elf32-m68k", "elf32-m68k") OUTPUT_ARCH(m68k) ENTRY(_start) SEARCH_DIR(/usr/m68k/m68k-elf/lib); /* Do we need any of these for elf? __DYNAMIC = 0; */ MEMORY { roma (rx) : ORIGIN = 0x00200000, LENGTH = 0x100000 } SECTIONS { /* Read-only sections, merged into text segment: */ . = 0x80000000 + SIZEOF_HEADERS; .interp : { *(.interp) } .hash : { *(.hash) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } .gnu.version : { *(.gnu.version) } .gnu.version_d : { *(.gnu.version_d) } .gnu.version_r : { *(.gnu.version_r) } .rel.init : { *(.rel.init) } .rela.init : { *(.rela.init) } .rel.text : { *(.rel.text) *(.rel.text.*) *(.rel.gnu.linkonce.t*) } .rela.text : { *(.rela.text) *(.rela.text.*) *(.rela.gnu.linkonce.t*) } .rel.fini : { *(.rel.fini) } .rela.fini : { *(.rela.fini) } .rel.rodata : { *(.rel.rodata) *(.rel.rodata.*) *(.rel.gnu.linkonce.r*) } .rela.rodata : { *(.rela.rodata) *(.rela.rodata.*) *(.rela.gnu.linkonce.r*) } .rel.data : { *(.rel.data) *(.rel.data.*) *(.rel.gnu.linkonce.d*) } .rela.data : { *(.rela.data) *(.rela.data.*) *(.rela.gnu.linkonce.d*) } .rel.ctors : { *(.rel.ctors) } .rela.ctors : { *(.rela.ctors) } .rel.dtors : { *(.rel.dtors) } .rela.dtors : { *(.rela.dtors) } .rel.got : { *(.rel.got) } .rela.got : { *(.rela.got) } .rel.sdata : { *(.rel.sdata) *(.rel.sdata.*) *(.rel.gnu.linkonce.s*) } .rela.sdata : { *(.rela.sdata) *(.rela.sdata.*) *(.rela.gnu.linkonce.s*) } .rel.sbss : { *(.rel.sbss) } .rela.sbss : { *(.rela.sbss) } .rel.bss : { *(.rel.bss) } .rela.bss : { *(.rela.bss) } .rel.plt : { *(.rel.plt) } .rela.plt : { *(.rela.plt) } .init : { KEEP (*(.init)) } =0x4e75 .plt : { *(.plt) } .text : { *(.text) *(.text.*) *(.stub) /* .gnu.warning sections are handled specially by elf32.em. */ *(.gnu.warning) *(.gnu.linkonce.t*) . = ALIGN(0x4); __CTOR_LIST__ = .; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) *(.ctors) LONG(0) __CTOR_END__ = .; __DTOR_LIST__ = .; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) *(.dtors) LONG(0) __DTOR_END__ = .; } =0x4e75 _etext = .; PROVIDE (etext = .); .fini : { KEEP (*(.fini)) } =0x4e75 .rodata : { *(.rodata) *(.rodata.*) *(.gnu.linkonce.r*) } .rodata1 : { *(.rodata1) } .gcc_except_table : { *(.gcc_except_table) } /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ .ctors ALIGN(4): { /* gcc uses crtbegin.o to find the start of the constructors, so we make sure it is first. Because this is a wildcard, it doesn't matter if the user does not actually link against crtbegin.o; the linker won't look for a file to match a wildcard. The wildcard also means that it doesn't matter which directory crtbegin.o is in. */ KEEP (*crtbegin.o(.ctors)) /* We don't want to include the .ctor section from from the crtend.o file until after the sorted ctors. The .ctor section from the crtend file contains the end of ctors marker and it must be last */ KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) } .dtors : { KEEP (*crtbegin.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) KEEP (*(SORT(.dtors.*))) KEEP (*(.dtors)) } . = ALIGN(0x10) + (. & (0x10 - 1)); .data : { *(.data) *(.data.*) *(.gnu.linkonce.d*) } .data1 : { *(.data1) } .eh_frame : { *(.eh_frame) } .got : { *(.got.plt) *(.got) } .dynamic : { *(.dynamic) } /* We want the small data sections together, so single-instruction offsets can access them all, and initialized data all before uninitialized, so we can shorten the on-disk segment size. */ .sdata : { *(.sdata) *(.sdata.*) *(.gnu.linkonce.s.*) } _edata = .; PROVIDE (edata = .); __bss_start = .; .sbss : { *(.dynsbss) *(.sbss) *(.sbss.*) *(.scommon) } .bss : { *(.dynbss) *(.bss) *(.bss.*) *(COMMON) /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the .bss section disappears because there are no input sections. */ . = ALIGN(32 / 8); } . = ALIGN(32 / 8); _end = .; PROVIDE (end = .); /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } /* These must appear regardless of . */ }
I know it sounds and feels like you are helping a delinquent because you are!

10

Mega Shocked (./9):
Thank you for seeing my SOS and reviewing my message in a bottle.

I tried your suggestion and of creating a P2 rom with some data in it.
Upon doing this I was met with challenges and revelations.


I know it sounds and feels like you are helping a delinquent because you are!


Hehe,,

yep, first step using 2MB instead of 1MB is a good and easy goal for a start. smile

Concerning your makefiles 3 suggestions:

1) Don't use and link any code into P2 or above , just data.. this means that you must avoid to link any library.

the line : LIBS = -lDATlib -lprocess -lc -lgcc is suspicious , code and libraries must be linked into 'P1' makefle *only*

2) Watch out about object files collisions your 'P2' makefile is linking over an object called test.o, i would recommend to output link results in different files per makefile (e.g : test1.o to test.9.o)


3) Keep 'P1' makefile as usual with everything required by Datlib there but keep 'P2' almost empty. (you should need only original neocart.x script and your OBJS as data.)

As an example the P2 link command could look like this :

gcc -L%NEODEV%\m68k\lib -m68000 -O3 -Wall -fomit-frame-pointer -ffast-math -fno-builtin -nostartfiles -nodefaultlibs -D__cart__ -Wl,-Map,foo2.map -T%NEODEV%\src\system\neocart.x %NEODEV%\tmp\_level01MapSprite.o -o test2.o
avatar

11

Well I can't thank you enough for opening this can of worms.
It has got me fascinated with the build process.

So first thanks for your tips! I got the P2 to compile!
At this point I don't know if it will work because I jumped to another tangent in the process but it did build with both my neocart.x and neocartBank.x

Over the years I kinda automated things in my own broken way so I got less and less familiar with the inner workings of the makefile.
Plus I never really had a good grasp of it to begin with.

That said I am slowly realizing what is going on!

My typical makefile uses neocart.x and I have seperate makefiles for the 4 Banks of gfx data I am using that do indeed use neocartBank.x

I know that the more GFX I compile the larger the P rom gets. Since the GFX were compiled separately and they do not appear to be linked in the makefile that builds the P1 I was curious of how the P1 knows to inflate based on more GFX being built?

I came to realize that the makefiles have a .map file in the linker line. My legacy files of NeoDev don't have that.

-Wl,-T$(BASEDIR)/src/system/neocartBank.x -Xlinker -Map=_bankMap.map


When I cracked the file open the file reads this way. (There is more to the file than what is seen here but I can only guess that the files is generated from "neocart.x" or "neocartBank.x"

Memory Configuration Name Origin Length Attributes roma 0x00000000 0x00100000 xr ram 0x00100000 0x0000f000 xrw romb 0x00200000 0x00100000 xr *default* 0x00000000 0xffffffff Linker script and memory map 0x80000074 .=(0x80000000+SIZEOF_HEADERS)
This leads me to ask based is the configuration above of the output.map file setup to accept 8X the P! or is it limiting the data to only reside in the first 1mb of the P rom?

I would love to unlock romb as I believe I am living a life where roma is doing all the heavy lifting and eventually maxes out at 1mb.

this isn't my first kick at the can.

I exceeded roma here and was confused because I believe there should be more data built into at least the first mb of romb but I was lost then and still am now. lol
Upon reviewing the post I think my confusion was I thought GFX banks were identical to P banks when infact there is a difference.

topics/190747-roma-is-full-banko-section-text#post-7


I believe DATLib does something comparable to the NgDataLinker with the parsemap utility provided. I have been able to access my bank data via including defines with memory locations.

However this now has me fascinated....

In my current case each of my 4 makefiles / .xml files basically create GFX data every 8mbs via adjusting the fillmode then the parsemap utility provides address information that doesn't overlap I guess because the fillmodes have gaps in them.
(I could be wrong with what I'm saying here... factually...I have always been able to build and access all 4 banks of GFX data and reviewing the address information provided in the generated .h files it does not appear that the addresses overlap.)

Why I'm fascinated is because maybe now I can leave the filmodes alone and maybe set them all to something like '256' (Bank 0 has this...just going with that)
I'm think this way because if the rig can accommodate a setup where each mb of P2 points to a different bank information do I need to stagger banks when compiling them?

P2_1 -> Bank0, 0-1mb
P2_2 -> Bank1, 0-1mb
P2_3 -> Bank2, 0-1mb
P2_4 -> Bank3, 0-1mb

Oh wow I still might be able to build map tables into 4 more banks on top of what I already have above! EX:P_5-P8!!!!!?

I am thinking this...
I do realize I am limiting myself (technically P2 can be used for any data) but I would love to see if this rig could work...

P1 is only used for code and hardcoded tables etc
P2 is exclusive to GFX

Would this setup actually allow for the mixing of GFX from different banks in the same scene?
Is the secret in adjusting the neocart.x and neocartBank.x files....

These are mysteries that swirl in my head.

I know that incorporating DATlib is a little off topic as all your work is custom built on top of NeoDev but you ignited a spark and excitement is looming.
Any DATLib users that have mastered the art of romb please chime in if you could help with the working the angles on a 8mb P rom!!!!

I'm hyped up! Thanks for being patient as I stumble ozzyyzzo!

12

I have no clue of what is going on with Datlib , i do not use it ^^

and yes you can use data from different banks in the same frame/scene, that's what the bank switching mecanism is used for.
avatar

13

Hi ozzyyzzo!

Thanks again for shedding some light on the power of the P's!

I wish I was a cooler poster and could delete some of the non-sense I wrote but it is historical record.

Your tools got me excited and I ripped my little environment apart in search of the true nature of what is going on with my rig and banking.

In the end I am banking with multiple 1mb P2 roms. My mame driver and conversion to NeoSD scripts are the tell tail signs.
In my case with DATLib I don't believe I can yet combine GFX from separate banks but I hope one day I will be able to!

Examples of how I use the banks now:
I can have one whole scene with Attract Mode GFX based on bank 0
Then another whole scene with Gameplay GFX based on bank 1 etc...
FMV is great cause you can just jump from one bank to the next like a flip book.
So there are benefits here forsure!

For reference for anyone reading my non-sense posts the official Mame Driver and NeoSD script I am using look like this.
I'm posting them if perhaps they are slightly useful for example purposes.
(Why didn't I review these before I began digging through my dev environment I'll never know. roll)

Mame Driver

<software name="game"> <description>Game Demo</description> <year>2016</year> <publisher>MegaShockers</publisher> <sharedfeat name="release" value="MVS,AES" /> <sharedfeat name="compatibility" value="MVS,AES" /> <part name="cart" interface="neo_cart"> <dataarea name="maincpu" width="16" endianness="big" size="0x500000"> <rom loadflag="load16_word" name="dev_p1.bin" offset="0x000000" size="0x100000" crc="bdda2c6e" sha1="6a94dee2d22feb07ea68a90ce67d5cac1b17b9c9" /> <rom loadflag="load16_word" name="Bank0.bin" offset="0x100000" size="0x100000" crc="d707ec72" sha1="6ca9b79e5679e49684cb8993f532966df3186ec9" /> <rom loadflag="load16_word" name="Bank1.bin" offset="0x200000" size="0x100000" crc="53bed158" sha1="625f9f78980b8272303bd49309ff502e1da68315" /> <rom loadflag="load16_word" name="Bank2.bin" offset="0x300000" size="0x100000" crc="53bed158" sha1="625f9f78980b8272303bd49309ff502e1da68315" /> <rom loadflag="load16_word" name="Bank3.bin" offset="0x400000" size="0x100000" crc="53bed158" sha1="625f9f78980b8272303bd49309ff502e1da68315" /> </dataarea> <dataarea name="fixed" size="0x040000"> <rom offset="0x000000" size="0x020000" name="fix.bin" crc="0e6a7c73" sha1="31b1194524dcc80ec4d63bac088b6fb4909f496c" /> </dataarea> <dataarea name="audiocpu" size="0x080000"> <rom offset="0x000000" size="0x080000" name="m1.M1" crc="da4878cf" sha1="ce13d18a4c5d01974df8542c67c4df00dbc6e7c1" /> </dataarea> <dataarea name="ymsnd" size="0x800000"> <rom name="v1.V1" offset="0x000000" size="0x800000" crc="2ced86df" sha1="d6b73d1f31efbd74fb745200d4dade5f80b71541" /> </dataarea> <dataarea name="sprites" size="0x2800000"> <rom loadflag="load16_word" name="bk0char.bin" offset="0x000000" size="0x800000" crc="a9bdc000" sha1="93b0dfcd2121ddf6ea1fe99514a176d76e4b0c98" /> <rom loadflag="load16_word" name="bk1char.bin" offset="0x800000" size="0x800000" crc="f8e21968" sha1="e103d4f59cd841267b882580aee338e99f192c3f" /> <rom loadflag="load16_word" name="bk2char.bin" offset="0x1000000" size="0x800000" crc="f8e21968" sha1="e103d4f59cd841267b882580aee338e99f192c3f" /> <rom loadflag="load16_word" name="bk3char.bin" offset="0x1800000" size="0x800000" crc="f8e21968" sha1="e103d4f59cd841267b882580aee338e99f192c3f" /> </dataarea> </part> </software>
NeoSD conversion script

SET ROMNAME=game SET MAMEPATH="C:\mame" SET MAMEROMPATH="C:\mame\roms\ SET PROJECTPATH="C:\NeoDev\src\samples\GAME" cd %MAMEROMPATH%%ROMNAME% romwak /f dev_p1.bin p1.p1 romwak /p p1.p1 p1.p1 1024 255 romwak /f BANK0.bin B0.bin romwak /p B0.bin B0.bin 1024 255 romwak /f BANK1.bin B1.bin romwak /p B1.bin B1.bin 1024 255 romwak /f BANK2.bin B2.bin romwak /p B2.bin B2.bin 1024 255 romwak /f BANK3.bin B3.bin romwak /p B3.bin B3.bin 1024 255 copy /b B0.bin + B1.bin + B2.bin + B3.bin p2.p2 romwak /p bk0Char.bin bk0C.bin 8192 255 romwak /p bk1Char.bin bk1C.bin 8192 255 romwak /p bk2Char.bin bk2C.bin 8192 255 romwak /p bk3Char.bin bk3C.bin 8192 255 CharSplit bk0C.bin -rom a1 CharSplit bk1C.bin -rom b1 CharSplit bk2C.bin -rom d1 CharSplit bk3C.bin -rom e1 ren a1.c1 c1.c1 ren a1.c2 c2.c2 ren b1.c1 c3.c3 ren b1.c2 c4.c4 ren d1.c1 c5.c5 ren d1.c2 c6.c6 ren e1.c1 c7.c7 ren e1.c2 c8.c8 copy /y fix.bin s1.s1 move p1.p1 NeoSDTemp move p2.p2 NeoSDTemp move s1.s1 NeoSDTemp copy m1.M1 NeoSDTemp copy v1.V1 NeoSDTemp copy v2.V2 NeoSDTemp copy v3.V3 NeoSDTemp move c1.c1 NeoSDTemp move c2.c2 NeoSDTemp move c3.c3 NeoSDTemp move c4.c4 NeoSDTemp move c5.c5 NeoSDTemp move c6.c6 NeoSDTemp move c7.c7 NeoSDTemp move c8.c8 NeoSDTemp del b0.bin del b1.bin del b2.bin del b3.bin del bk0C.bin del bk1C.bin del bk2C.bin del bk3C.bin CScript zip.vbs C:\mame\roms\game\NeoSDTemp C:\mame\NeoSD\Exports\DEMO_%1.zip del /q C:\mame\roms\game\NeoSDTemp\*.* cd C:\mame\NeoSD NeoBuilder -n DEMO_%1 -m DEMO -y 2021 -g Action Exports\DEMO_%1.zip START C:\mame\NeoSD cd %PROJECTPATH%

14

i'm glad that u made it works smile

i really hope that this will bring neogeo homebrews to another level , bring the excitement to devs and fun to the community!

now prepare yourself for world domination ^^
avatar

15

Hi

I am sure the bankswitching will be one of the best feature available for the future.

Sorry if that question was already answered before, but does it work on real hardware ? Is it supported by the common linkers ? (Neo SD, Darksoft Multi) And does it make the PCB manufacturing more complex ?

16

works on NeoSD without problems! (except that the NeoBuilder.exe is a bit buggy)