It is currently Mon Dec 11, 2017 4:33 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 92 posts ]  Go to page Previous  1 ... 3, 4, 5, 6, 7  Next
Author Message
PostPosted: Mon Sep 25, 2017 6:00 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 262
tepples wrote:
Oziphantom wrote:
The best way is to probably do what the guy in the video did and make a i386 to 6502 cross assembler and use the current compilers.

Which raises the question of how to bias the current compilers away from operations that are cheap on i386 but expensive on 6502, such as 32-bit arithmetic.
Either force the programmer to use 16bit maths for int and use long for 32bit maths and then downgrade in the conversion or use gcc and -m16 flag to put it into 16bit compatible mode. Also I said best not good ;)

I wonder how good say the last version of the Borland 8086 compiler was.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 6:03 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 262
Memblers wrote:
Going back to DementedPurple's original question, could it be that we're massively over-complicating the issue by even talking about the boot sequence and all that? It's kind of a long way of saying that if you're using a modern system-on-a-chip, that rules out bare-metal programming. You absolutely can buy a microcontroller and have it run only 100% your own hand-crafted code, but if you're assigned the Rasp Pi for a school project, you'll just have to work within that.

Maybe this is naive, but wouldn't it be possible to just make a program like this?

Code:
int main(void)
{
 asm_program(); // assembly code in a separate file
}


And then link your assembly code in? In your asm code, if you need to use the libraries associated with the hardware, then you'd learn how to call the C function from asm, as well as how to accept return values when needed. And I guess in your asm you would just declare those function names as extern, global, or something. You'd have to include them in an .H file I suppose. And it would all get sorted out at link time, right?

There's also the option of doing inline assembly, but so far whenever I've looked at that for GCC it just looks disgustingly ugly (if you're wanting to write more than a few lines for it). Having to put every line in quotes, and adding \n to the end of every line, ugh. It's almost more characters of compiler syntax than there is of assembly code. And if I understand correctly, with inline asm there's also the issue of the compiler dicking with your code. Like if you read the same memory or register twice in a row, the compiler would just optimize it to one read. Good luck polling a register like that. Of course the solution is to declare it "volatile", but I'm just pointing out that it's probably better to just make a separate .ASM file that gets linked in, if you're doing anything significant in assembly.

I don't know if that helps, but those are my simple thoughts on how you'd program a Raspberry Pi in assembly. Personally, I'd say learn the assembly if you want to, but C is useful and is used pretty much everywhere. When you're used to C, it's much faster to code something in it versus assembly, I doubt anyone would argue otherwise. It certainly worth learning. Seems like mostly people complain about having trouble understanding pointers, but if you know assembly and indirect addressing, you're doing to have no trouble understanding pointers.

100% you can do this, however I feel that Demanded Purple is more looking to hit the PI like a NES and just write something to a register or RAM to get something on the screen so they don't have to deal with complicated APIs to draw things on the screen and the like, rather than actually write in ASM over C.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 6:22 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 262
Bregalad wrote:
Quote:
In a lot of other high level languages you don't even have options about whether something is passed by value or reference.

In my personal opinion, I think a good language should never allow a subroutine to modify any of it's argument. Problem solved, arguments are passed by either value or reference, it doesn't make any difference. If you need the value to be changed, you should be required to do an explicit copy. This makes things clearer at high level thinking AND it allows for maximum optimisation of code generation.

The problem with C is that it will only inline or optimize routine if you add "static" keyword which actually means local. This was introduced only recently, and wasn't present in the original C language where they didn't allow any call optimisation, so originally C programs had to push all their calling arguments on the data stack in all cases.


I disagree. As sometimes I want to return multiple things from a function call, say bool myFunc(int param1, int* result) this allows me to get a result AND know if it succeed without having a magic unsafe value like INT_MIN. And no throwing an Exception is not a good way to solve the problem its bad and annoying one ;)
Passing a short is faster by value than by reference. If I want to pass in a 64K array then by reference is best, copying 64K is a drain on performance.
It is worth noting at this point C doesn't have by reference that is a c++ thing ;)

This does give me more control as now I can pass
Code:
int
int*
int**
const int
const int*
const int**
int* const
int** const
const int* const
const int** const
const int*const*
const int*const* const

To create the exact level of code protection I need. Ignoring the fact you can just cast const away ;)
These are for protection and forcing an API not an optimisation. Given int myFunc(int i) { return i+5; } while I do not mark i as const, the compiler will work out it is const and optimise for it automatically, I can put the const word there to stop me or somebody else in future modifying i but that won't change how the function is compiled.
Likewise the inlining of functions is 100% up to the compiler and the compiler will inline as it sees fit, the myfunc above on a release build with -02 or higher would most certainly be inlined as just an add op-code in the parent code without me putting inline on the prototype. There is a case on gcc that you can basically force it to inline if you have static and inline in the name but this is more a quirk rather than a standard, the static is there to ensure the linker doesn't make multiple instances of it and hence all of them get folded into the same case. Seeing as C doesn't have classes everything is Static by definition.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 7:46 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7312
Location: Chexbres, VD, Switzerland
Oziphantom wrote:
I disagree. As sometimes I want to return multiple things from a function call, say bool myFunc(int param1, int* result) this allows me to get a result AND know if it succeed without having a magic unsafe value like INT_MIN.

In assembly, I do this all the time, and it's a trivial thing to do. I just use the A register for the result and the C flag indicates wether a correct result is returned. Couldn't be any simpler.

In C you have to ressort to a "hack" of passing a poitner to a function which is not a real argument, but a return value. This seriously hinder code readability. In addition this removes possibilities of optimisation. WHY, oh good lord why ? You just showed how terrible C is. If I could just return an "int, bool" pair, that'd be much better ! But C can only do that with structs, which are their own complicated thing in their own right.
Quote:
This does give me more control as now I can pass
Code:
int
int*
int**
const int
const int*
const int**
int* const
int** const
const int* const
const int** const
const int*const*
const int*const* const

You just summarized how broken C is in a few lines. This garbage basically don't even look like code.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 9:11 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5886
Location: Canada
Oziphantom wrote:
rainwarrior wrote:
In a lot of other high level languages you don't even have options about whether something is passed by value or reference.

It is worth noting at this point C doesn't have by reference that is a c++ thing ;)

To clarify what I meant, when I said "pass by reference", I was referring only to the difference between passing "by reference" or "by value". If you pass a pointer to a function and use that pointer to reference an object, this is still the "pass by reference" idiom. I wasn't referring to the C++ & argument modifier (usually equivalent to the pointer but with its "value" part hidden).

You could insist that C only has "pass by value" by some definitions, but I'm just trying to clarify the meaning of my words in this thread.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 9:48 am 
Offline

Joined: Tue Feb 07, 2017 2:03 am
Posts: 262
Bregalad wrote:
Oziphantom wrote:
I disagree. As sometimes I want to return multiple things from a function call, say bool myFunc(int param1, int* result) this allows me to get a result AND know if it succeed without having a magic unsafe value like INT_MIN.

In assembly, I do this all the time, and it's a trivial thing to do. I just use the A register for the result and the C flag indicates wether a correct result is returned. Couldn't be any simpler.

In C you have to ressort to a "hack" of passing a poitner to a function which is not a real argument, but a return value. This seriously hinder code readability. In addition this removes possibilities of optimisation. WHY, oh good lord why ? You just showed how terrible C is. If I could just return an "int, bool" pair, that'd be much better ! But C can only do that with structs, which are their own complicated thing in their own right.
What optimisation has been removed? Its not assembly there is not a 1:1 relationship between what you type and what is spat out. There is nothing to stop the C compiler from returning the Bool in C and *result in A. There is also nothing stopping it from inline the code such that it never returns anything and the Bool gets turned into a Jump or branch further down the code and the *result is just generated in A, an optimisation you will never get with a function in ASM. Maybe it decideds it is called so much that it puts a static variable to hold the value on the heap and thus just writes the value to that variable and all code there after loads from variable to save it from having to push and pop it on the stack multiple times, if your code needs to save and refer to the returned value a lot in the code that follows your calls. Just because something is in the function params or a return from a function doesn't mean it has to be called or passed that way, the ABI can define a standard way, but the optimiser will do it any darn way it wants, as it seems to be most optimal for all cases it is used. However if you really want to nudge __fast_call will use Pascal passing as default over a default C, but I doubt compilers even listen to it any more either, but maybe they do in a debug build.

Also which of these is more readable
Code:
returnValues = my_func(value)
if returnValues[0]:
    print( returnValues[1]

or
Code:
int funcValue = 0;
if (myFunc(param, &funcValue) )
{
   printf("%d\n",funcValue);
}

much of a muchness? lets try a larger example
Code:
returnValues = get_containing_rect(point1x,point1y,point2x,point2y,width1,height1,width2,height2)
if returnValues[0]:
    collide_point(point_x,point_y,returnValues[1],returnValues[2],returnValues[3],returnValues[4])

or
Code:
int rect_x =0, rect_y = 0, rect_width = 0, rect_height = 0;
if( get_containing_rect(point1x,point1y,point2x,point2y,width1,height1,width2,height2,&rect_x,&rect_y,&rect_width,&rect_height)
{
   collide_point(point_x,point_y,rect_x,rect_y,rect_width,rect_height) ;
}

the C case ( and in the 2nd case use a struct ) has named things for each param the earlier case has magic numbers that index into something to pull something out. The named version is going to be a lot easier to read and maintain. Also in the C case the compiler can put 2 dummies on the stack, then push the rect_x,rect_y,rect_width,_rect_height, thus when it wants to call collide_point it just overwrites the dummies with point_x and point_y and optimises the push and pull away. as poping 4 or 6 things off the stack takes the same amount of time. Which it can't do in the multiple return case as you have fixed index looks up in the array, I mean it can modify the index to switch over if it has put each item on the stack and not put an object on the stack that is a real list or something...

Bregalad wrote:
Quote:
This does give me more control as now I can pass
Code:
int
int*
int**
const int
const int*
const int**
int* const
int** const
const int* const
const int** const
const int*const*
const int*const* const

You just summarized how broken C is in a few lines. This garbage basically don't even look like code.
Why is being able to strictly control what a function can and can't do to a variable broken. It alows you to make the definition exact and binding, it allows you to get the compiler to catch bugs for you, the static analyser to help find issues, it allows you to make code thread safe. Its when you get to a language that has seemingly random rules on what does change something passed in and what won't change. See Python, String can't change, List can change , Tuple can't change, dictionary can change, Byte array can't change, byteArray can change. In C it is explicit and written in the prototype for all to see and if you try to break it, the compiler will stop you. Not give you a random crash when person clicks button C then D, then A then F.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 10:10 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2982
Location: Tampere, Finland
Oziphantom wrote:
Also which of these is more readable
Code:
returnValues = my_func(value)
if returnValues[0]:
    print( returnValues[1]

or
Code:
int funcValue = 0;
if (myFunc(param, &funcValue) )
{
   printf("%d\n",funcValue);
}


Python has tuple unpacking, so you can do:
Code:
foo1, foo2 = my_func(v)
if foo1:
  print foo2

C++17 allows something similar through "structured bindings".

It becomes unwieldy with more than a few return values though. At that point it's better to starting using structs. (The C++17 structured bindings allow one to unpack structs as well, which is pretty nice.)

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 10:43 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7312
Location: Chexbres, VD, Switzerland
Oziphantom wrote:
What optimisation has been removed? Its not assembly there is not a 1:1 relationship between what you type and what is spat out. There is nothing to stop the C compiler from returning the Bool in C and *result in A.

Passing a pointer to a function removes possibilities for optimization. The caller doesn't know what the callee will do with the pointer, so the variable has to be in RAM (it cannot be in a register), so that pointing to it is possible. It has to either push the RAM address on the stack or putting it in a register, before calling the function.

The calee has to use this argument and write to this address. The compiler doesn't know what the adress is and doesn't know it's a variable in another piece of code. The explicit pointer kills the opportunity of an optimization. It's better to let language manage memory their own way and optimize it rather than requiring the user to manually managing memory.

In practice, yes with modern copiler they could do a lot of optimizing effort and see under some conditions that it's possible to optimize this out. I didn't veryfy since then, but I'm pretty sure GCC 4 could *not* optimize this, and passed the pointer to the calee, even if doing so was avoidable.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 11:46 am 
Offline

Joined: Mon Apr 01, 2013 11:17 pm
Posts: 437
rainwarrior wrote:
I've never heard of the optimizer doing anything inside inline assembly code. ?? That's actually the point of using inline assembly: to get exactly the code you specified. Volatile applies to C, not inline assembly.

The optimizer can't change the inline assembly code, but it can change where and when said code is used. GCC assumes by default that any block of inline assembly will produce the same output given a specific set of inputs, with no side effects. If that's not true, you need the volatile keyword. (Inline assembly with no outputs is always volatile on the assumption that it must have a side effect; inline assembly with no outputs and no side effects would do nothing and be removed by the optimizer.)

rainwarrior wrote:
MSVC didn't try to do it with strings, and it looked a lot cleaner, by the way:

Sure, but you trade some potential optimizations for that cleaner syntax. GCC's horrible syntax allows you to directly inform GCC about how it may optimize your code, in a way that doesn't require GCC to parse assembly for every CPU it supports.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 1:44 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 5886
Location: Canada
Bregalad wrote:
Passing a pointer to a function removes possibilities for optimization. The caller doesn't know what the callee will do with the pointer, so the variable has to be in RAM (it cannot be in a register), so that pointing to it is possible. It has to either push the RAM address on the stack or putting it in a register, before calling the function.

The calee has to use this argument and write to this address. The compiler doesn't know what the adress is and doesn't know it's a variable in another piece of code. The explicit pointer kills the opportunity of an optimization. It's better to let language manage memory their own way and optimize it rather than requiring the user to manually managing memory.

In practice, yes with modern copiler they could do a lot of optimizing effort and see under some conditions that it's possible to optimize this out. I didn't veryfy since then, but I'm pretty sure GCC 4 could *not* optimize this, and passed the pointer to the calee, even if doing so was avoidable.

Using a pointer instead of a value is a potential optimization by itself, i.e. reduces the call preamble (less arguments to push to the stack). This may be a tradeoff against how much that pointer is dereferenced inside the function. Putting a structure in registers or the stack is not necessarily faster or slower, it could be either.

Your suggestion that being able to specify which of these two behaviours you want somehow removes an optimization opportunity... I don't understand at all. If it can be inlined/etc. the pointer dereferences can be inlined and optimized away, but otherwise there would need to be some mechanism for the compiler/language to identify which way of passing arguments is overall faster. (This is not necessarily a decidable question; e.g. you could have dereferencing of the pointer inside a loop with length dependent on an argument, which could not be determined statically.)

So the question here again is, what language are you comparing against? Of the languages I know that don't have this kind of distinction, none of them have any special capability to optimize subroutine calls in this way, to my knowledge.

C letting you specify means that you can make this determination yourself where static analysis is impossible. "I know this loop is going to be run 500-700x per call, usually." It makes manual optimization possible where static optimization was not. A gain, not a loss?

Joe wrote:
rainwarrior wrote:
I've never heard of the optimizer doing anything inside inline assembly code. ?? That's actually the point of using inline assembly: to get exactly the code you specified. Volatile applies to C, not inline assembly.

The optimizer can't change the inline assembly code, but it can change where and when said code is used. GCC assumes by default that any block of inline assembly will produce the same output given a specific set of inputs, with no side effects. If that's not true, you need the volatile keyword. (Inline assembly with no outputs is always volatile on the assumption that it must have a side effect; inline assembly with no outputs and no side effects would do nothing and be removed by the optimizer.)

Ah, so the volatile keyword has a special meaning when applied as a qualifier to the asm block. I understand now, thanks. I thought memblers was suggesting using it within the inline assembly somehow.

Joe wrote:
Sure, but you trade some potential optimizations for that cleaner syntax. GCC's horrible syntax allows you to directly inform GCC about how it may optimize your code, in a way that doesn't require GCC to parse assembly for every CPU it supports.

You're referring to MSVC's __asm not having a way to specify inputs/outputs/clobber, and letting the assembler deduce it instead from the code inside? (This has not much to do with the problem of using string literals that started this discussion... heh...)

Honestly I'm not sure which of these methods is better in practice, though. asm being more error prone and ugly, and __asm being less customizable? You're already optimizing "by hand" in either case, so whatever missed opportunity for optimizing code around the block I guess could be addressed by expanding the scope of it a little...

In the last several years though, I've seen a lot less inline assembly and a lot more intrinsics/builtins which I think are usually a better fit for the kind of problems where I used to go for inline assembly.


Top
 Profile  
 
PostPosted: Mon Sep 25, 2017 11:39 pm 
Offline

Joined: Thu Jul 23, 2015 7:54 pm
Posts: 152
Location: USA
Just wanna give my two cents on all this.

OP, besides 6502 assembly, what other languages do you know? I'm just curious because your opinions on higher level languages and not wanting to use libraries remind me of myself when I first got into nesdev. If you're up to the challenge, go for it, although i think you may be in a bit over your head. Assembly is fun, but I'm honestly pretty happy we don't have to use it that much in modern day computing. It still is kinda sad that it seems to be becoming a lost art in a sense.

As for this however-many-page debate going on: I honestly feel like C is the best balance between assembly and the huge abstraction of other high level languages that there is. As Bregalad pointed out (unbeknownst to me) the compiler still does a lot that the programmer doesn't know about, but in my opinion at least (and to an extent of course) I don't really care about micro-optimizations when I'm coding in anything other than assembly, unless its something trivial.

C++ was the first "real" language I learned, but after learning C, there are a lot of things I don't like about it. Scope resolution syntax lile "std::" looks ugly as hell in my opinion. Templates are about the furthest I've delved into the language (or plan to.); everything past that just seems like unnecessary bloat / kludges. Are any of these other "aspects" of C++ used that often?


Top
 Profile  
 
PostPosted: Tue Sep 26, 2017 12:48 am 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1046
It seems interesting to not care about micro optimizations, and yet still care about perceived bloat. I don't mess with too many modern C++ features yet, but I don't mind them being there.

I like new over malloc, fstreams over fopen, functions in my "structs", constructors/destructors. I had actually written my own code for vectors, not realizing they already existed. Vectors are also pretty sweet.

I'm not sure I can express how much destructors (and vectors) do for allowing one to think less about memory allocation.

I don't like cout over printf, but in C++ I can use printf and in C I can't use new. So I use C++ over C, primarily for small things. I used to not get the "object oriented" methodology, until I realized that the "best practices" rules of thumb I picked up while coding in assembly language were very related. (Not sure... I can put how into words well.)

I still write c on occasion, just for the fun of tiny c compiler, but I don't have much reason to not use C++ even if there was nothing I liked about it over C except new. C++ does seem to offer a lot of crazy things lately, but you can pretty much always avoid them and write pretty c-like c++.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Sep 26, 2017 6:10 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19322
Location: NE Indiana, USA (NTSC)
Sogona wrote:
your opinions on higher level languages and not wanting to use libraries remind me of myself when I first got into nesdev.

Or someone worries about not enough space in an NROM-128 project for the credit required by the license.

Kasumi wrote:
It seems interesting to not care about micro optimizations, and yet still care about perceived bloat.

Or someone doesn't care about micro-optimizations for speed but does care about them for size in an NROM-128 project.

Kasumi wrote:
I like new over malloc, fstreams over fopen

I tried fstreams over fopen using GNU libstdc++ several years ago. A single, simple use of ostringstream gave me a 180K Hello World binary on a Game Boy Advance, which has only 288K of RAM (256K of bulk RAM and 32K of fast RAM) for RAM-based programs. I looked into it, and the linker was failing to remove unreachable code related to date, time, and money formatting. This forces practical C++ programs that use iostream on that platform to be ROM-based, which makes use by players 2 through 4 in a multiplayer match more expensive. (GBA supports multiplayer with one cartridge so long as the program running on the machines of players 2 through 4 is RAM-based.) In addition, low-end memory card adapters such as the GBA Movie Player can run only RAM-based programs.


Top
 Profile  
 
PostPosted: Tue Sep 26, 2017 9:21 am 
Offline
User avatar

Joined: Mon Jan 03, 2005 10:36 am
Posts: 2982
Location: Tampere, Finland
Kasumi wrote:
I don't like cout over printf, but in C++ I can use printf and in C I can't use new. So I use C++ over C, primarily for small things.

This library is pretty sweet: https://github.com/fmtlib/fmt

_________________
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: kkfos.aspekt.fi


Top
 Profile  
 
PostPosted: Tue Sep 26, 2017 9:47 am 
Offline

Joined: Mon Apr 01, 2013 11:17 pm
Posts: 437
rainwarrior wrote:
Honestly I'm not sure which of these methods is better in practice, though. asm being more error prone and ugly, and __asm being less customizable? You're already optimizing "by hand" in either case, so whatever missed opportunity for optimizing code around the block I guess could be addressed by expanding the scope of it a little...

Sure, but the more you write in assembly the less the compiler can optimize for you, and you might want to be able to change the optimization profile for different CPUs.

Either way, it's a tradeoff: MSVC's __asm is easier to write and debug, GCC's asm() is better integrated with the optimizer.

rainwarrior wrote:
In the last several years though, I've seen a lot less inline assembly and a lot more intrinsics/builtins which I think are usually a better fit for the kind of problems where I used to go for inline assembly.

They're the best of both worlds: easy to read/write, and fully integrated with the compiler.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 92 posts ]  Go to page Previous  1 ... 3, 4, 5, 6, 7  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 9 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group