This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Spec for HLL targeting NES

Spec for HLL targeting NES
by on (#97396)
Edit: Back to working on this, made some real progress, spec attached below. Some of the post below is outdated.

Before posting this, I went back and reread this thread (Among others) to see if there were any ideas I overlooked.

http://nesdev.com/bbs/viewtopic.php?t=7976

The op's ideas were pretty close to my own: The syntax is going to be very simple, almost BASIC-like, but it does adapt things from C:

-Pointers are a separate type and must be declared. Uses Indirect-Y.
-Structs are very much in play. An array of structs would use Direct Indexing.
-Local variables will be simulated, but there's no stack manipulation with Indirect.

The plan is to generate asm roughly close to what would be hand-written.

Some issues that might crop-up (Or, things from C that didn't make it):
-No 24-bit or 32-bit vars: These will be implemented when the compiler targets 16-bit cpus. What do you call a 24-bit variable when 'int' is reserved for 32-bits and 'long' for 64-bits? For now, the carry flag is available if more than 16-bits are needed.

-1D arrays only: Readily maps to processor addressing modes. On x86 and Arm, 2D and more arrays would be trivial with their multiply instruction, but Noism will not be targeting those platforms. You'll have to use a pointer table to simulate 2D arrays.

One change to the spec: I'm not crazy about retaining any feature of C pointer syntax, so
ptr pointer_var = &var

might become

ptr pointer_var = [var]

Also, forgot negation (~).

Reasons to use Noism:
-Scoping removes the need to juggle temporary variables
-Create a portable code base for NES and GB (Others to be added later)
-Syntax maps closely to a handful of asm instructions

Right now the compiler does syntax error checking. Once I get the compiler functional, the plan is to release it with at least one demo that will run on both NES and GB. Looking forward to feedback -- Hopefully someone else will use it.

by on (#97397)
I have a note on the whole idea. HLL is a High Level Language, i.e. very abstracted from the low level, the hardware. So there is a dillema - either you target it for effective resulting code, but this brings some hardware limitations back to the abstaction level (like 1D arrays and lack of 32 bit math), thus compicating use of the language, or you target it to simplify programming a lot, hiding all these details, but this leads to not very effective code.

Another thing, my personal opinion is that some new language, that has syntax far from a popular one (BASIC, C, Java) is doomed to be used by the author and maybe a few other people only - just because people couldn't use their previous experience well, they also can't use this experience later, and it is difficult to get help on an unpopular, new thing.

by on (#97399)
This is no doubt an experiment. But if nothing else, it's pretty fun to work on a really simple compiler. The first point I agree with. Effective code is the aim here. One way a xD array might be simulated is to restrict the higher dimensions to a power of 2. Then again, that will result in a crazy amount of bit shifts. The compiler could also create and load the pointer table automatically; I don't like the idea of surprise code, so that might be enabled with a preprocessor option.

I may just go ahead and include 32-bit math. But 24-bit vars are also a necessity.

The second point I see where you're coming from but not really. Most programming languages demand experienced programmers change their habits a bit. Lua had the gall to break the tradition of indexing an array with zero, and those Blizzard guys love it. This language will never be popular anyway unless it gets new people into deving on old systems.

Also, please ignore the embarrassing self-contradiction in section XIV. of the spec.

by on (#97400)
What's wrong with C style syntax?

by on (#97401)
Nothing is really wrong with C-style syntax itself, but C itself has some really backwards features in it.

Lack of forward declarations is the single most annoying part of C and C++. I write a function, then need to copy-paste the first line somewhere else just so I can call it in code that happens to be before the function. That is absolutely ridiculous.

Also, C doesn't have a good way for a function to return multiple values. You can return a struct, but that mainly leads to the compiler throwing it on the stack instead of returning it in several registers.

What else is wrong with C and C++? Assignments in If expression. Infinite while loops because you accidentally put a semicolon before the open brace. The postincrement operator having undefined meaning when there is more than one use of that variable. The wrong order of operations makes bitwise arithmetic lower priority than expected (OR should be like addition, AND and bit shifts should be like multiplication), but they are all low priority instead. Leading zeroes magically make your numbers octal. Tons of annoying legacy crap.

by on (#97402)
Mostly because using pointers without '*' and '&' looks cleaner, and I'm trying to reduce usage of the shift-key.

by on (#97403)
Dwedit wrote:
Lack of forward declarations is the single most annoying part of C and C++. I write a function, then need to copy-paste the first line somewhere else just so I can call it in code that happens to be before the function. That is absolutely ridiculous.

I agree with you on this one !

Quote:
Also, C doesn't have a good way for a function to return multiple values. You can return a struct, but that mainly leads to the compiler throwing it on the stack instead of returning it in several registers.

To return two values, return a long and bit pack both values in the result.
For more than two values, have a pointer in the argument list that points to where you'd like the function to write its results.

by on (#97404)
Yeah, things like that can be cumbersome in C. But if I created a new language like this, I would probably keep the syntax as close to C as I could. It will mean the language is easy to pick up for anyone who has done a little programming. Noism doesn't even use return values as far as I can see? Sort of like only allowing for void functions in a C like world.

I do like this project, btw. Since I think assembly is very complicated to do large logic stuff in, this will probably make things easier.

I have been using CC65 a lot, and while it does what I want most of the time, there are some things that really make me want to try something else.

If noism will make it simpler that CC65 to handle bank switching, it can be really useful. I don't have much experience working with bank switching, but in CC65 you basically have to keep your code small enough to fit in one 16K bank, and use the other one for pure data.

If noism also generates more efficient code than CC65, that is also a great thing.

Keep up the good work.

by on (#97405)
Quote:
I have been using CC65 a lot, and while it does what I want most of the time, there are some things that really make me want to try something else.

Could you tell us more ?
I had this idea of porting sdcc for the 6502 not long ago, but it will be a large project and I'm not sure I can handle it.
Re: Spec for HLL targeting NES
by on (#97407)
strat wrote:
Some issues that might crop-up (Or, things from C that didn't make it):
-No 24-bit or 32-bit vars: These will be implemented when the compiler targets 16-bit cpus. What do you call a 24-bit variable when 'int' is reserved for 32-bits and 'long' for 64-bits?

Most C platforms I know of with 16-bit int have 32-bit long. You could use the <stdint.h> names int16_t, uint16_t, int32_t, and uint32_t for variables that stay 16-bit or 32-bit regardless of platform. C doesn't define a 24-bit integer type, but int24_t and uint24_t would be least astonishing to programmers, and they can typedef it to something more convenient (like the s32, u32, s16, and u16 commonly seen in GBA code).

Quote:
Reasons to use Noism:
[...]
-Create a portable code base for NES and GB (Others to be added later)

Yay! No more DRY violations that are characteristic of ports to some platforms. And if you provide a standard C back end, the result becomes more easily portable to Windows, Mac OS X, desktop Linux, iOS, and Android.

Dwedit wrote:
What else is wrong with C and C++? Assignments in If expression. Infinite while loops because you accidentally put a semicolon before the open brace.

I believe several compilers have warnings against that. For example, one would get two warnings for code like this:
Code:
while (pointer = getNext()) ;

Both of which could be suppressed by making the intent clearer:
Code:
while ((pointer = getNext()) != NULL) { }

Quote:
The postincrement operator having undefined meaning when there is more than one use of that variable.

Then why not put your increments on another line?

Bregalad wrote:
To return two values, return a long and bit pack both values in the result.

Not if the values you want to return won't fit in a long. For example, a pointer typically takes up a whole long (sizeof(intptr_t) >= sizeof(long)). Passing a pointer to a struct is far more common in code that I've read, even for two results.

by on (#97409)
Nioreh wrote:
If noism will make it simpler that CC65 to handle bank switching, it can be really useful. I don't have much experience working with bank switching, but in CC65 you basically have to keep your code small enough to fit in one 16K bank, and use the other one for pure data.

It should be fine to have code in switchable banks in CC65, just make sure the library routines (like stack manipulation) are in the fixed bank. This can be achieved by naming the fixed bank/segment "CODE". Of course you have to manually make sure the correct functions are mapped in the non-fixed bank whenever calling them. :)
Re: Spec for HLL targeting NES
by on (#97429)
Quote:
Most C platforms I know of with 16-bit int have 32-bit long. You could use the <stdint.h> names int16_t, uint16_t, int32_t, and uint32_t for variables that stay 16-bit or 32-bit regardless of platform.


Thinking it over real quick, maybe it's best to adopt the GBA syntax as default: s8, ... s64. The 's' is supposed to stand for 'signed' but it might as well be 'storage', since cpus afaik don't distinguish between signed and unsigned, only the 'printf' function.

Quote:
Yay! No more DRY violations that are characteristic of ports to some platforms. And if you provide a standard C back end, the result becomes more easily portable to Windows, Mac OS X, desktop Linux, iOS, and Android.


Hmmm... having read your XNA article, my best interpretation of this idea is that Noism compiles into C code. That's not really a bad idea. Then you'd have one code base that will create a real NES game and a retro-style game for highend systems. Too bad we didn't have this discussion while Megaman 9 was being developed.
Re: Spec for HLL targeting NES
by on (#97432)
strat wrote:
Thinking it over real quick, maybe it's best to adopt the GBA [integer type names] as default: s8, ... s64. The 's' is supposed to stand for 'signed' but it might as well be 'storage', since cpus afaik don't distinguish between signed and unsigned, only the 'printf' function.

An 8*8=16 bit multiply, or 16*16=32, or 32*32=64 sure does. So do the operators /, <, and >.
Re: Spec for HLL targeting NES
by on (#101209)
I'm glad to see my previous work here is still being read :D I'll be interested to see what you come up with! I did get mine producing assembly code, but did not pursue it much beyond that.

What I discovered in my toying around (and never really reported back on) was that the HLL I had designed simply could not produce machine code that was as efficient (or even close) to the code I would write by hand. This was due to the fact that the HLL and the machine were engineered for different patterns.

So, if your goal is to produce machine code that is as efficient or very close to the assembly you would write by hand, you need to identify the patterns you are using while writing assembly and then base the requirements of the HLL on those patterns.

If you want a common code base for multiple platforms then you're best bet is to use a small set of basic patterns to base your HLL on, then translate those into machine instructions for the target platform that may not necessarily be very efficient.

I think trying to achieve both is not terribly productive on these early microprocessor architectures. These things (the 65xx and Z80 series MC's) were specifically engineered to be programmed in their machine language. Other architectures (like the Intel 80 series and later Motorola 68K series) were designed with HLL's in mind, and efficiently implement some of these HLL patterns in hardware.
Re:
by on (#101211)
Bregalad wrote:
Dwedit wrote:
Lack of forward declarations is the single most annoying part of C and C++. I write a function, then need to copy-paste the first line somewhere else just so I can call it in code that happens to be before the function. That is absolutely ridiculous.

I agree with you on this one !


Me too! I hate that crap, it's so unneeded. I also hate how there's no real way to include data for the binary to use at runtime, you have to load it from a file and stick it in an array or something. I hate how modern languages work in general honestly. C isn't too bad, but still, could be much better.
Re: Re:
by on (#101212)
Dwedit wrote:
Lack of forward declarations is the single most annoying part of C and C++. I write a function, then need to copy-paste the first line somewhere else just so I can call it in code that happens to be before the function. That is absolutely ridiculous.

I thought a prototype like that was a forward declaration. You have to do the same thing in ca65 if you want to call a function defined in another translation unit: a .global or .import statement in the caller, and a .global or .export statement in the callee.

3gengames wrote:
I also hate how there's no real way to include data for the binary to use at runtime, you have to load it from a file and stick it in an array or something. I hate how modern languages work in general honestly. C isn't too bad, but still, could be much better.

The GBA homebrew community adopted two patterns: converting the data to assembly language at compile time ("bin2s"), and my solution of appending the data to the executable in an archive format ("GBFS").
Re:
by on (#101221)
For multiplication, you could add an option to use MMC5 hardware multiplication if it is available.

Dwedit wrote:
Nothing is really wrong with C-style syntax itself, but C itself has some really backwards features in it.
Yes there are some.

Quote:
Lack of forward declarations is the single most annoying part of C and C++. I write a function, then need to copy-paste the first line somewhere else just so I can call it in code that happens to be before the function. That is absolutely ridiculous.
It doesn't annoy me much, but this is a valid point.

Quote:
Also, C doesn't have a good way for a function to return multiple values. You can return a struct, but that mainly leads to the compiler throwing it on the stack instead of returning it in several registers.
Yes, I agree; other programming languages do, even though things like Haskell also would return in a structure (or a pair), in Forth to return on the stack, etc.

Quote:
What else is wrong with C and C++? Assignments in If expression.
I like this feature actually.

Quote:
Infinite while loops because you accidentally put a semicolon before the open brace.
I like this feature too.

Quote:
The postincrement operator having undefined meaning when there is more than one use of that variable.
I don't think it is wrong; what else would expected in such cases?

Quote:
The wrong order of operations makes bitwise arithmetic lower priority than expected (OR should be like addition, AND and bit shifts should be like multiplication), but they are all low priority instead.
This I agree with you; I don't like this either.

Quote:
Leading zeroes magically make your numbers octal. Tons of annoying legacy crap.
I think it is OK.

Shiru wrote:
I have a note on the whole idea. HLL is a High Level Language, i.e. very abstracted from the low level, the hardware. So there is a dillema - either you target it for effective resulting code, but this brings some hardware limitations back to the abstaction level (like 1D arrays and lack of 32 bit math), thus compicating use of the language, or you target it to simplify programming a lot, hiding all these details, but this leads to not very effective code.
What if you add commands for both purpose, and add only the stuff to the compiled binary in the places needed for adding the extra details?
Re:
by on (#101227)
thefox wrote:
Nioreh wrote:
If noism will make it simpler that CC65 to handle bank switching, it can be really useful. I don't have much experience working with bank switching, but in CC65 you basically have to keep your code small enough to fit in one 16K bank, and use the other one for pure data.

It should be fine to have code in switchable banks in CC65, just make sure the library routines (like stack manipulation) are in the fixed bank. This can be achieved by naming the fixed bank/segment "CODE". Of course you have to manually make sure the correct functions are mapped in the non-fixed bank whenever calling them. :)


I'm currently trying out AOROM/BNROM mappers with cc65. The 32k banks means it's easier to arrange code, though it mostly precludes having DPCM samples (which I'm fine with).

Currently I'm putting C code in one bank only, and other banks are used for data or assembly code only. I link each bank separately. cc65's debug symbol output uses the PC to identify their location, rather than the ROM location, so to separate these it's best to build banks individually and combine them in a later step.

My ZP/BSS usage for assembly code is in a global include, so the assembly banks won't conflict. C stuff is allowed to use the rest of RAM. I would have to portion each separately if I was to put C in more than one bank; would probably be a bit of a pain. I would also have to make sure do the DATA segment initialization for each bank that has C code.

I dunno, so far I haven't run out of code space in the C bank; putting music/graphics/levels in other banks leaves me with a decent amount of room there.
Re: Spec for HLL targeting NES
by on (#101232)
The OP hasn't posted in a few months and his rapidshare link to the tentative specs are gone. I really wish someone would complete an Atlan/Scratchalan/nBasic-like project :p
Re: Spec for HLL targeting NES
by on (#101233)
I'm working on and and or in my conditional statements for the IF ca65 macro. It's designed to be as good as raw assembly could be - I'll update it sometime this week. Maybe that will make things highlevel enough for you along with a nice easy to read assignment macro (math could be added..) ? What else are you looking for? ca65 is probably the most flexible solution for coding NES/6502 due to the way it tracks namespaces and segments. Otherwise maybe you should consider cc65.
Re: Spec for HLL targeting NES
by on (#101235)
Movax12 wrote:
I'm working on and and or in my conditional statements for the IF ca65 macro. It's designed to be as good as raw assembly could be - I'll update it sometime this week. Maybe that will make things highlevel enough for you along with a nice easy to read assignment macro (math could be added..) ? What else are you looking for? ca65 is probably the most flexible solution for coding NES/6502 due to the way it tracks namespaces and segments. Otherwise maybe you should consider cc65.


I should definitely consider NESICIDE/cc65. That's as good as it gets. I'm still a little perplexed that batari BASIC for Atari 2600 is still a one hit wonder though :)
http://bataribasic.com/

The basic premise seems to be a generic kernel with some conversion of subroutines and mathematical statements to asm - which in turn get compiled. I almost never go over CPU cycles and it's BASIC.
Re: Spec for HLL targeting NES
by on (#101244)
batari Basic looks pretty cool. Anyone have experience using it and could give some impressions?
Re: Re:
by on (#101248)
rainwarrior wrote:
cc65's debug symbol output uses the PC to identify their location, rather than the ROM location, so to separate these it's best to build banks individually and combine them in a later step.

Are you talking about the VICE label listing file that can be generated? In the generated debug file of the snapshot version, you can get the ROM location from any symbol, too, but you have to (or should) use the dbginfo C library to parse that file (this is included in the CC65 source package).
Re: Re:
by on (#101253)
thefox wrote:
rainwarrior wrote:
cc65's debug symbol output uses the PC to identify their location, rather than the ROM location, so to separate these it's best to build banks individually and combine them in a later step.

Are you talking about the VICE label listing file that can be generated? In the generated debug file of the snapshot version, you can get the ROM location from any symbol, too, but you have to (or should) use the dbginfo C library to parse that file (this is included in the CC65 source package).


I've been using the last release, never tried the snapshot. Glad to hear that more info is getting into the debug output in newer versions. I wrote a short python script to parse the VICE label file into FCEUX debug files.

So, I guess whenever the next release is it'd be more reasonable to put everything into one link, which would make it easier to keep the RAM usage consistent across the banks (wouldn't have to manage space explicitly for each one; everything could link its variables into the same BSS segment). You'd probably be able to get C in multiple banks in mapper with a fixed bank that you can put the CRT into. One of the problems with AxROM is the you can't link the CRT in two different banks without a conflict (at least, not in one link). Anyhow, still hasn't come up-- I'll consider it whenever I actually exceed 32k of C code.
Re: Spec for HLL targeting NES
by on (#101379)
qbradq wrote:
batari Basic looks pretty cool. Anyone have experience using it and could give some impressions?


I tried it once awhile back (While looking into vcs dev) and really just screwed around with some example code. Attempting to increase the granularity of the playfield "pixels" caused the rom to crash. But it should be really good if you can think of something to do with it. One game made with batari is a homebrew release.
http://www.atariage.com/software_page.h ... areID=4107
Re: Spec for HLL targeting NES
by on (#101381)
slobu wrote:
The OP hasn't posted in a few months and his rapidshare link to the tentative specs are gone. I really wish someone would complete an Atlan/Scratchalan/nBasic-like project :p


I got distracted by another pet-project, but what's really eating my free time of all things is the PS1 "classic" Xenogears. But with this topic coming back up, I feel a little push now.

One big snag was how to circumvent a software stack, because that will make the project a bit redundant with C. What I'm going to try is a function tree that allocates bytes for parameters and local vars to a set of functions that do not share the same call chain. This means function pointers can't be passed to other functions (But they can be used locally like a switch statement). If a function is only called once or only global vars are passed to it, no extra bytes for params are used.

The tentative specs had some goofy stuff anyway, so once the compiler gets further along new specs will go up. A new plan is to incorporate signed and unsigned types since I now understand the 'overflow' flag. Data types will be 8-64 bits wide (including 24-bit). 2D arrays are still unlikely, though.
Re: Spec for HLL targeting NES
by on (#101386)
Glad this project isn't indefinitely stalled :)

I see that most successful projects parse a relaxed language into assembly:
batari Basic http://bataribasic.com/
BasiEgaXorz http://devster.monkeeh.com/sega/basiegaxorz/
ZX Basic for SMS http://www.smspower.org/forums/viewtopic.php?t=12902

Regardless if it has a software stack or not it wouldn't be redundant to C if programmers could focus on functional code rather than syntax and formatting.
Re: Spec for HLL targeting NES
by on (#101387)
Have you ever heard of JAL? It's a vaguely-pascal-like language that targets the PIC16 line fairly efficiently, despite PIC16's lack of particularly useful addressing modes and only having a single first-class register.

edit: make link point to wikipedia page instead of 7-years-stale CVS repository
Re: Spec for HLL targeting NES
by on (#101388)
JAL looks alot like batari Basic. VERY, very cool. As a side I wish they'd just make it compatible with an Arduino Uno instead of trying to make yet another sorta-Arduino.
Re: Spec for HLL targeting NES
by on (#101391)
It's a good ... 10? 15? years older than the arduino :P
Re: Spec for HLL targeting NES
by on (#101401)
I'm still tweaking it but I have some good stuff working for ca65, at least I think so .. :)

I have the ability to use IF()-ELSE-ENDIF blocks and WHILE()-DO-ENDWHILE, DO-WHILE() (and alternate syntax with REPEAT-UNTIL)

I can produce very efficient code, with the only limitations being that all ORs are first, then any ANDs.

[You can place one OR block on the end in brackets - due to the recursive nature of the macro code you cannot have more than one test on the left of an AND or OR, such as (cond1 || cond2) && cond3 ]

Brackets are not needed - sort of - I'll explain more when I am finished

Example:

Code:
if ((buttonpressed BUTTON_A) && ((comp a > #$12) || (comp a < #$8))
  ; do stuff
endif

;OR

do
  ;code
  dex
while not zero && bit6 clear


Of course there are a few other caveats (functions that could be called could destroy flags you want to check), but for the most part, if you understand basically what it is doing, it is very fast and will use all BXX instructions as expected rather than creating boolean tests.
Re: Spec for HLL targeting NES
by on (#121783)
I've finally made substantial progress, to the point it generates some workable assembly. Here's a sample:

Code:
   index = 0
   ii2 = 0
   limit = 15
   s8 src[20]
   s8 dst[20]
   while ii2 < limit
      dst[index] = src[ii2]
      index = index + 1
      ii2 = ii2 + 1
   endw   


6502 output:
Code:
        lda #0
        sta index
        sta ii2
        lda #15
        sta limit
        jmp CHECK_0
LOOP_0:
        ldy ii2
        lda src,Y
        ldx index
        sta dst,X
        inc index
        inc ii2
CHECK_0:
        lda ii2
        cmp limit
        bcc LOOP_0
ENDWHILE_0:


Of course this output isn't optimal - the finished compiler will do a second pass to keep the index vars in the x and y registers. Mostly this is to let people who were interested know I'm back to working on it. There's an updated spec in the original post. Since uc65 is now live and open source, it might be tempting to simply add the features I was planning for Noism. Except I don't like Java that much. Here's a complete progress report:

Fully implemented:
Syntax parsing and symbol generation
Line comments and block comments
Flow control and while/dowhile looping
Nested parenthetical operations
Indexed array accesses
Pointer declaration and addressing
Variable declaration
Function calls and returns
Number format checking
Basic syntax checking (i.e. can a symbol follow the previous symbol)
Output of pseudo-asm syntax, used for testing features and converting to target cpu

Partially implemented:
6502 asm output
Structures
Scoping and static allocation of space for inner-scoped variables

Not started yet:
Function pointers
.asm file output
Complete error checking
Compiler directives
Multi-byte variable accesses
Signed numbers
16-bit/multibyte code output
for looping
z80 asm output
Re: Spec for HLL targeting NES
by on (#121784)
I had completely forgotten about this project. It's nice to see you have made some progress.
Re: Spec for HLL targeting NES
by on (#121787)
thefox wrote:
I had completely forgotten about this project. It's nice to see you have made some progress.

I also had forgotten.
It looks like your code generation is pretty good !!
One thing it could do is to see that the check "ii2 < limit" will always be true on it's first iteration, and transform the while loop into a do-while loop, saving a useless jump in the code.
However I admit it's not as simple as it sound to implement.

Way better than the pure crap that CC65 generates (the same code would generate several kilobytes of code using indirect addressing all the time when you didn't even use pointers in the original code).
Re: Spec for HLL targeting NES
by on (#121791)
Bregalad wrote:
Way better than the pure crap that CC65 generates (the same code would generate several kilobytes of code using indirect addressing all the time when you didn't even use pointers in the original code).


Examples of the crap by cc65:

Code:
void test(void)
{
   unsigned char index = 0;
   unsigned char ii2 = 0;
   unsigned char limit = 15;
   unsigned char  src[20];
   unsigned char  dst[20];
   while (ii2 < limit)
   {
      dst[index] = src[ii2];
      ++index;
      ++ii2;
   }
}


generates a crap indeed:

Code:
; ---------------------------------------------------------------
; void __near__ test (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _test: near

.segment   "CODE"

   lda     #$00
   jsr     pusha
   jsr     pusha
   lda     #$0F
   jsr     pusha
   ldy     #$28
   jsr     subysp
L0015:   ldy     #$29
   lda     (sp),y
   dey
   cmp     (sp),y
   bcs     L0016
   lda     sp
   ldx     sp+1
   ldy     #$2A
   clc
   adc     (sp),y
   bcc     L001A
   inx
L001A:   jsr     pushax
   lda     sp
   ldx     sp+1
   clc
   adc     #$16
   bcc     L001D
   inx
L001D:   ldy     #$2B
   clc
   adc     (sp),y
   bcc     L001E
   inx
L001E:   sta     ptr1
   stx     ptr1+1
   ldy     #$00
   lda     (ptr1),y
   jsr     staspidx
   ldy     #$2A
   clc
   lda     #$01
   adc     (sp),y
   sta     (sp),y
   dey
   clc
   lda     #$01
   adc     (sp),y
   sta     (sp),y
   jmp     L0015
L0016:   ldy     #$2B
   jmp     addysp

.endproc


However,

Code:
void test(void)
{
   static unsigned char index = 0;
   static unsigned char ii2 = 0;
   static unsigned char limit = 15;
   static unsigned char  src[20];
   static unsigned char  dst[20];
   while (ii2 < limit)
   {
      dst[index] = src[ii2];
      ++index;
      ++ii2;
   }
}


generates (DATA and BSS sections omited):

Code:
; ---------------------------------------------------------------
; void __near__ test (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _test: near

.segment   "CODE"

L001A:   lda     L0012
   cmp     L0014
   bcs     L001B
   lda     #<(L0019)
   ldx     #>(L0019)
   clc
   adc     L0010
   bcc     L001F
   inx
L001F:   sta     ptr1
   stx     ptr1+1
   ldy     L0012
   lda     L0017,y
   ldy     #$00
   sta     (ptr1),y
   inc     L0010
   inc     L0012
   jmp     L001A
L001B:   rts

.endproc
Re: Spec for HLL targeting NES
by on (#121792)
Glad to see you're making progress!
Re: Spec for HLL targeting NES
by on (#121798)
Looks good.
Bregalad wrote:
...saving a useless jump in the code.

I agree with this. I can't see a need to jump down to the branch. Have the branch at the start of the loop. Maybe I am missing something?
Re: Spec for HLL targeting NES
by on (#121800)
What you're missing is that this optimization is complicated to implement (while perfectly possible).
For all declared & initialized variables, there is need to have the initial value itself memorized in the compiler itself. If the variables are ever affected with anything other than a constant literal value (or another value that reduces to a constant literal) then it should be marked as "scratched".

Then when a while or for loop is encountered, the test condition has to be checked at compile time against the variables. If anything in the expression tree is marked as "scratched" then you can perrform no optimisation. Else you can evaluate the expression fully, and it'll either evaluate to "true" or "false".
If it evaluates to "false" then you can remove the loop completely (and it'd be a good idea to issue a warning to the user, as it's probably an error of his side).
If it evaluates to "true" then you can turn it into a do-while loop, so you avoid a dummy check at loop entering.

Note that this is my interpretation of the problem, the actual implementation might need to be even more complex than this short description.

@Shiru : Wow, CC65 can produce such code ? Declaring the variables as "static" makes so much a difference ? I'm very impressed :shock:
Definitely far to optimal code, but it's somewhat decent compared as to what it produces under normal conditions.
I should try to use CC65 again and declare everything "static" and see how it turns out.

Still it should be doable to hack the compiler so that everything is "static". Although this keywoard always confused the heck out of me in C. As far as I undestand it can have completely different meaning depending on where it's used. With GCC "static" global variables are invisible to other files, and "static" functions can use nonstandard calling convention (leading often to much more optimal code). In fact all functions should be static ideally but then you can give up separate compilation and intermixing with other languages.
Re: Spec for HLL targeting NES
by on (#121802)
Bregalad wrote:
Still it should be doable to hack the compiler so that everything is "static".
Look at the compile-time flag -Cl
Re: Spec for HLL targeting NES
by on (#121805)
Bregalad wrote:
As far as I undestand it can have completely different meaning depending on where it's used. With GCC "static" global variables are invisible to other files, and "static" functions can use nonstandard calling convention (leading often to much more optimal code). In fact all functions should be static ideally but then you can give up separate compilation and intermixing with other languages.

Yes, in global scope static makes the identifier (variable or a function) local to the module (file). In function scope it basically makes the variable a global variable in function scope.

Many compilers nowadays have link time code optimization, so I don't think your "ideal" of all functions being static is that useful in the end.

Also there's a slight difference between local static variables and the -Cl switch of cc65. When local static variables are used, the variable is only initialized once at the beginning of the program. When -Cl is used, the variables are initialized every time the function is entered.
Re: Spec for HLL targeting NES
by on (#121809)
Thanks for continuing forward on this project. There is definitely a target audience for this project and I'm thrilled to be one =)
Re: Spec for HLL targeting NES
by on (#121815)
Bregalad wrote:
Then when a while or for loop is encountered, the test condition has to be checked at compile time against the variables.


I see what you are saying. I was thinking this would be better:

Code:
LOOP_0:
        lda ii2
        cmp limit
        bcs ENDWHILE_0
        ldy ii2
        lda src,Y
        ldx index
        sta dst,X
        inc index
        inc ii2
        jmp LOOP_0
ENDWHILE_0:


But this way a JMP is required each loop rather than just the first. I may have to rethink the way I do some things.
Re: Spec for HLL targeting NES
by on (#121818)
Thanks for the encouragement, y'all.

'dowhile' eliminates the jump to the first conditional check; the updated spec will mention that.

Quote:
I agree with this. I can't see a need to jump down to the branch. Have the branch at the start of the loop. Maybe I am missing something?


If you need to test the condition before the loop runs at all, it's better to jump down there than to have the branch at the head of the loop, jumping past the loop if the test fails. Then the actual loop has to keep jumping back to the test. Ironically, this is one of the few sensible constructs of cc65.

The one optimization I'm really concerned with is eliminating any lda/sta/cmp/inc instruction that can be replaced with ldy/sty/cpy/iny and if the index var needs arithmetic, using tay in place of sta/ldy. I've got a good idea of how this will be implemented. Also, another one I'd like to implement is removing the cmp operation for countdown loops. (cc65 to its credit also eliminates cmp when beq/bne works).

Code:
   while ind > 0
      ind = ind - 1
      ii2 = ii2 - 1
   endw
   
   LOOP_0:
      dec ii2
      dec ind
      bne LOOP_0


That second example of cc65 output still makes me glad to be working on this. I actually made a map-scrolling demo in C earlier this year, just to see if working on a whole new language was even worthwhile, and the most tiresome thing was flagging each var as local or global with a comment. cc65 doesn't allow anonymous unions, so that's also a little inconvenient though maybe tolerable.
Re: Spec for HLL targeting NES
by on (#121819)
Quote:
But this way a JMP is required each loop rather than just the first.


Yeah, I took extra care to make the compiler not output loops like this.
Re: Spec for HLL targeting NES
by on (#121842)
strat wrote:
Also, another one I'd like to implement is removing the cmp operation for countdown loops.

If you know your start and end values, this code snippet works for a FOR loop (8-bit, in ca65 macro code):
Code:
 
; calculate the first invalid value (if this value is found at the test, exit the loop)
_END_COUNTER_VALUE_  = < ( ( _STEP_ + _TO_VALUE_) + ( _FROM_VALUE_ - _TO_VALUE_ .mod _STEP_) )

; if high bit of 2nd iteration and high bit of 2nd LAST iteration are high AND END_COUNTER_VALUE_ is positive

.if ( (_FROM_VALUE_ + _STEP_ ) & ( _END_COUNTER_VALUE_ - _STEP_ ) & $80 ) .and
    (.not (_END_COUNTER_VALUE_ & $80))
            BMI START_LABEL
; if high bit of 2nd iteration and high bit of 2nd LAST iteration are low AND END_COUNTER_VALUE_ is negative
.elseif (.not (((_FROM_VALUE_ + _STEP_ ) | ( _END_COUNTER_VALUE_ - _STEP_ )) & $80 )) .and
        (_END_COUNTER_VALUE_ & $80)
            BPL START_LABEL
; last value is zero
.elseif _END_COUNTER_VALUE_ = 0
            BNE START_LABEL
.else
    ; need to check loop against end value:
    .if .xmatch(_COUNTERVAR_, x) ; found X
        cpx #_END_COUNTER_VALUE_
    .elseif .xmatch(_COUNTERVAR_, y) ; found y
        cpy #_END_COUNTER_VALUE_
    .elseif .xmatch(_COUNTERVAR_, a) ; found a
        cmp #_END_COUNTER_VALUE_
    .else
        lda _COUNTERVAR_
        cmp #_END_COUNTER_VALUE_
    .endif
    BNE START_LABEL
.endif
Re: Spec for HLL targeting NES
by on (#122833)
Here's a video preview of the compiler in action (If you have a crappy connection like I do, be sure to watch in full screen @360P). Basically this is to demonstrate how X and Y are used as register variables; if more than two indices are used in the same function, X is reserved for the most referenced index var. I also want to show off in the third example how X is used to initialize the other vars. This is closer to what a programmer normally does. :D

http://www.youtube.com/watch?v=nuM2GnUWVss