It is currently Mon Nov 19, 2018 4:39 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 23 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Wed Jun 27, 2018 4:10 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6956
Location: Canada
DRW wrote:
Is there any way I can force the compiler to treat this as a byte?

Only the result of (src+3) matters, you don't have to cast everything else. Cast has higher precedence than >> or most operators so you don't need extra parentheses either (unless you prefer them to make the order clear.)

This works for me:
Code:
;
; i = (unsigned char)(j+3) >> 2;
;
   lda     _j
   clc
   adc     #$03
   lsr     a
   lsr     a
   sta     _i
;

(i and j as zpsym)


Top
 Profile  
 
PostPosted: Wed Jun 27, 2018 4:18 pm 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1735
Thanks. Yeah, this seems to work.

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Wed Jun 27, 2018 6:57 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2049
Location: Fukuoka, Japan
Since we are talking about unnecessary code for pointers, I could talk about one of the result of my tests. One of my function that is processing intensive data requires to be done in asm. The parameters are for an entity that we put in a buffered list so it will be set later in the OAM when parsing is over. Instead of passing the parameters to the function or one my one with some with some ZP variables, what I do is I share a block of memory from the asm side and map it on the C side with a struct. This way all parameters, pointer included, are accessed with an indexer and cc65 doesn't use it's ptr1 thingy.

The c code would look that way when imported:

Code:
someheader.h

typedef struct {
    char x;
    char y;
    char foo;
    const char* data;
} myParmeters_t;

extern myParameters_t addToBufferParams;
#pragma zpsym("adToBufferParams");

void __fastcall__ addToBuffer(void);


when used:
Code:
   mycode.c

   // ---- begin addToBuffer -----
   addToBufferParams.x = actor.x;
   addToBufferParams.y = actor.y;
   addToBufferParams.foo = actor.foo;
   addToBufferParams.data = actor.currentFrame;
   addToBuffer();
   // ----- end addToBuffer ---------


Finally, the assembler would look something like this:
Code:

.export _addToBuffer := subAddToBuffer

.exportzp _addToBufferParams := zpParams


.segment "ZEROPAGE"
zpPrams:    .res 10   ; this is a shared buffer for parameters

.segment "BSS"
bufferedList  .res 60 ; some list of entitites

.segment "CODE"

;->BEGIN----------------------------------------------------------------------
; add to entity buffer
;
; Note: uses shared parameters (etc etc)
;
.proc subAddToBuffer
;---------------- Parameters definitions ----------------
.scope local
     posX = zpParams
     posY = zpParams+1
     foo  = zpParams+2
     data = zpParams+3
.endscope
;---------------------------------------------------------

     ; ... some code before
     lda local::posY
     ; do some processing
     sta bufferedList,x
     ; .... mode code here

     rts
.endproc
;-<END------------------------------------------------------------------------


The code generated on the C side access the shared buffer with indexers (_addToBufferParams,x) for all parameters so it should be fast enough. But, it may be possible that different structs may generate different access code so more testing would be required.

One thing that may not be relevant to this conversation but found interesting is that cc65 doesn't use signed char by default: everything is unsigned unless you pass a parameter to the compiler. Which means, unless you want to write portable code (which is maybe not that possible with the nes), you don't have to write unsigned char since char is unsigned. My definition of variable were becoming long with all those const that I may decide to remove the unecessary unsigned for now. I'm just used to write it out of habit.

edit:
I confused it with some other tests, it was not with an indexer but like this:

Code:
; addTobufferedListParams.data = hero.frame.current;
518 ;
519         .dbg    line, "src/example1.c", 201
520         lda     _hero+7+1
521         sta     _addTobufferedListParams+3+1
522         lda     _hero+7
523         sta     _addTobufferedListParams+3


edit2:
Updated the asm code since it did something else. This code is written by end for example only.


Top
 Profile  
 
PostPosted: Thu Jun 28, 2018 12:44 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1735
Yeah, I immediately wondered about your statement that simple struct members are accessed with an indexer. Since the location of the member is known at compile time, it's of course simply a +.


I didn't know that char was always unsigned in cc65. I would say this goes against the C standard.

In my case, it doesn't really make a difference. One of the first things that I included into my C code was:
Code:
typedef unsigned char byte;
typedef signed char sbyte;
typedef byte bool;
#define false 0
#define true 1


By the way, you should be really careful about sharing struct data with Assembly code. Because you always need to make sure that the data actually matches between the two.

I've had a similar issue where I wanted to use my struct for the current character data in Assembly, but it's not possible to actually export the member names as constants, so that they can be included in Assembly.

That's why I'm using inline assembly whenever I need the performance of Assembly with the data of a struct:
Code:
/* C version: */
En = Player.Energy;

/* Inline Assembly in C: */
__asm__("LDA %v + %b", Player, offsetof(struct GameCharacter, Energy));
__asm__("STA %v", En);

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Thu Jun 28, 2018 6:20 am 
Offline
Formerly ~J-@D!~
User avatar

Joined: Sun Mar 12, 2006 12:36 am
Posts: 474
Location: Rive nord de Montréal
DRW wrote:
I didn't know that char was always unsigned in cc65. I would say this goes against the C standard.

The signedness of the char type is implementation-dependent; that means it can be either signed or unsigned, and the implementer of the compiler is free to choose one. Oddly enough, whatever the signedness, it is not equivalent to the corresponding signed/unsigned char. That is, char, unsigned char and signed char are always distinct types.

You may want to use types in the stdint.h header, if you want portable integers of known sizes you can use these handy typedefs: int8_t, uint8_t, uint16_t etc.

_________________
((λ (x) (x x)) (λ (x) (x x)))


Top
 Profile  
 
PostPosted: Thu Jun 28, 2018 6:31 am 
Offline
User avatar

Joined: Sat Sep 07, 2013 2:59 pm
Posts: 1735
https://en.cppreference.com/w/c/language/arithmetic_types wrote:
char - type for character representation. Equivalent to either signed char or unsigned char (which one is implementation-defined and may be controlled by a compiler commandline switch), but char is a distinct type, different from both signed char both unsigned char

So which one is it? Equivalent to either one of the two or always distinct from both?

_________________
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg


Top
 Profile  
 
PostPosted: Thu Jun 28, 2018 7:33 am 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2049
Location: Fukuoka, Japan
Regarding c65, I think it is mentioned here:

http://cc65.github.io/doc/cc65.html#toc7.15
Quote:
7.15 #pragma signed-chars ([push,] on|off)

Changes the signedness of the default character type. If the argument is "on", default characters are signed, otherwise characters are unsigned. The compiler default is to make characters unsigned since this creates a lot better code. This default may be overridden by the --signed-chars command line option.

The #pragma understands the push and pop parameters as explained above.

As for standards, is seems to be c89 with a few things from c99:
Quote:
--standard std

This option allows to set the language standard supported. The argument is one of

c89

This disables anything that is illegal in C89/C90. Among those things are // comments and the non-standard keywords without underscores. Please note that cc65 is not a fully C89 compliant compiler despite this option. A few more things (like floats) are missing.
c99

This enables a few features from the C99 standard. With this option, // comments are allowed. It will also cause warnings and even errors in a few situations that are allowed with --standard c89. For example, a call to a function without a prototype is an error in this mode.
cc65

This is the default mode. It is like c99 mode, but additional features are enabled. Among these are "void data", non-standard keywords without the underlines, unnamed function parameters and the requirement for main() to return an int.

Please note that the compiler does not support the C99 standard and never will. c99 mode is actually c89 mode with a few selected C99 extensions.


edit:

I forgot about the main point talked, using some mapped values with struct. Yes, there is some risks but it simplify a lot of access so I guess if the person is organized it should be usable. Of course, when there is a bug with overlapping values then the fun begins .. ^^;;; I guess it a compromise between speed, usability and risk of weird bugs.


Top
 Profile  
 
PostPosted: Wed Jul 11, 2018 11:11 pm 
Offline
User avatar

Joined: Fri Nov 24, 2017 2:40 pm
Posts: 85
I'm a bit late to the party, but register variables can be a good solution to the original problem.

For instance, this function avoids copying the pointer into a scratch location. You can mark function arguments as register too, and it generates slightly different code.
Code:
void bar(void){
   register u8 *foo;
   *foo = 5;
}


Accessing the pointer is cheaper now, but the downside is there is kind of a lot of code (like 20 bytes) associated with saving and restoring register variables. So use them carefully.

Code:
;
; register u8 *foo;
;
   lda     regbank+14
   ldx     regbank+15
   jsr     pushax
;
; *foo = 5;
;
   lda     #$05
   ldy     #$00
   sta     (regbank+14),y
;
; }
;
   ldy     #$00
   lda     (sp),y
   sta     regbank+14
   iny
   lda     (sp),y
   sta     regbank+15
   jmp     incsp2


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page Previous  1, 2

All times are UTC - 7 hours


Who is online

Users browsing this forum: toggle switch and 1 guest


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