News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_ronaldo

#CPCtelera 1.4.2. release

Started by ronaldo, 11:59, 11 May 15

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

mr_lou

Quote from: FloppySoftware on 16:32, 13 July 15What's the purpose of the indicated fragment of code?

If I don't add it, the compiler will give a warning about an unreferenced variable. No biggie.

FloppySoftware

Quote from: mr_lou on 16:57, 13 July 15
If I don't add it, the compiler will give a warning about an unreferenced variable. No biggie.

But it's an expression, and may be it generate some code, unless SDCC takes care of this.
floppysoftware.es < NEW URL!!!
cpm-connections.blogspot.com.es

mr_lou

Quote from: FloppySoftware on 17:13, 13 July 15But it's an expression, and may be it generate some code, unless SDCC takes care of this.

Omitting it makes no change in the main.asm file.

FloppySoftware

I think it's worth to have a look on this:

FORUM / SDCC / [Sdcc-user] Access local variables with inline assembly (z80)


char foo(unsigned char c)
{
        char e;
        e = c;
        e += 5;
        return e;
}

generates this...


;sample.c:1: char foo(unsigned char c)
;       ---------------------------------
; Function foo
; ---------------------------------
_foo_start::
_foo:
        push    ix
        ld      ix,#0
        add     ix,sp
;sample.c:4: e = c;
;sample.c:5: e += 5;
        ld      a, 4 (ix)
        add     a,#0x05
        ld      l,a
;sample.c:6: return e;
        pop     ix
        ret
_foo_end::
        .area _CODE
        .area _CABS


Specially this part:



        push    ix
        ld      ix,#0
        add     ix,sp
floppysoftware.es < NEW URL!!!
cpm-connections.blogspot.com.es

mr_lou

Nothing new there.

The first 3 lines are usually added automatically. But they aren't added for me this time.
I tried adding them myself manually, but that just crashes everything.

ronaldo

@mr_lou: Do not assume that SDCC is going to generate any given code for accessing parameters. It might be different even on different executions of the compiler. Moreover, if you add this code, you are modifying IX register, which is used internally by SDCC sometimes and you will be creating lateral effects. Whenever using IX, it is important to save it on the stack and recovering it later on.

If you want to create a complete assembly function, create a separate .s file and write all the code in assembly. Check the way CPCtelera functions are implemented for reference: for instance, have a look at cpct_drawTileAligned2x8 or cpct_getHWColour, which recover parameters from stack in different ways.

And, if you wanted to POKE a value into memory, the easiest way to do it is this:

   *((u8*)0xC000) = 255;

which generates this appropriate code:

        ld      hl,#0xC000
        ld      (hl),#0xFF

If you wanted to check the code that SDCC generates, take a look at .asm and .lst files on obj/ folder. There you can check that SDCC is not generating the code you thought for accessing parameters.

mr_lou

Thanks ronaldo.

I was wondering/looking for a way to poke using just pointers earlier. So thanks for giving me that.

Right now though, I'm just trying to understand how to make functions with assembler, and I'm failing miserably.
Several things just makes no sense to me.

CPCTelera itself is great. As long as I can settle for using the CPCTelera functions.
But as soon as I have to add some stuff myself, I fail as miserably as I did 4 years ago too.

Take just now. You've just told me how to poke using pointers, so I immediately try this:


u16 addr;
for (addr = 0xc000; addr < 0xffff; addr++) {
  *((u8*) addr) = 255;
}


I expect it to fill the screen with pen 3. But instead it crashes everything.
(And please don't tell me I don't need to do it like that. I am just experimenting and trying to figure it out. But I very rarely can get things working the way I expect them to).
Looking at main.asm doesn't help me much as I don't understand Assembler.

ronaldo

Well man, I understand you. Learning takes time and understanding things requires failing and debugging constantly. All of us are constantly doing that when we work to expand our knowledge. I know it is frustrating, but I don't know a better way. Anyways, you need to be proud of your efforts: each bit of knowledge acquired with effort is worth it :).

Your code has a typing problem. If you want to access memory, you don't want an integer, but a pointer. In this case, you want a pointer to unsigned bytes (u8) elements (accessing memory bytes one by one). Try this:

u8* addr;
for (addr = 0xc000; addr < 0xffff; addr++) {
  *addr = 255;
}

That should work.

Anyways, your previous code, even having a typing confusion, should work well. However, SDCC seems to do something quite bizarre:

;src/main.c:41: for (addr = 0xc000; addr < 0xffff; addr++) {
        ld      de,#0x3FFF
00107$:
;src/main.c:42: *((u8*) addr) = 255;
        dec     de
        ld      l, e
        ld      h, d
        ld      (hl),#0xFF
        ld      h,e
        ld      l,d
        ld      e, h
;src/main.c:41: for (addr = 0xc000; addr < 0xffff; addr++) {
        ld      a,l
        ld      d,a
        or      a,h
        jr      NZ,00107$

It initializes DE to 0x3FFF and goes decreasing it, copying it to HL and using HL to write FF to memory. It effectively deletes memory from 0x3FFF to 0x0000, which makes no sense at all.

To me this seems like an SDCC bug, kind of a confusion with types.

mr_lou

Quote from: ronaldo on 18:00, 13 July 15It effectively deletes memory from 0x3FFF to 0x0000, which makes no sense at all.

You have no idea how good that makes me feel.
Means there's hope for me yet.  :)

gerald

Quote from: ronaldo on 18:00, 13 July 15
Well man, I understand you. Learning takes time and understanding things requires failing and debugging constantly. All of us are constantly doing that when we work to expand our knowledge. I know it is frustrating, but I don't know a better way. Anyways, you need to be proud of your efforts: each bit of knowledge acquired with effort is worth it :) .

Your code has a typing problem. If you want to access memory, you don't want an integer, but a pointer. In this case, you want a pointer to unsigned bytes (u8) elements (accessing memory bytes one by one). Try this:

u8* addr;
for (addr = 0xc000; addr < 0xffff; addr++) {
  *addr = 255;
}

That should work.

Anyways, your previous code, even having a typing confusion, should work well. However, SDCC seems to do something quite bizarre:

;src/main.c:41: for (addr = 0xc000; addr < 0xffff; addr++) {
        ld      de,#0x3FFF
00107$:
;src/main.c:42: *((u8*) addr) = 255;
        dec     de
        ld      l, e
        ld      h, d
        ld      (hl),#0xFF
        ld      h,e
        ld      l,d
        ld      e, h
;src/main.c:41: for (addr = 0xc000; addr < 0xffff; addr++) {
        ld      a,l
        ld      d,a
        or      a,h
        jr      NZ,00107$

It initializes DE to 0x3FFF and goes decreasing it, copying it to HL and using HL to write FF to memory. It effectively deletes memory from 0x3FFF to 0x0000, which makes no sense at all.

To me this seems like an SDCC bug, kind of a confusion with types.
Putting the bug aside,  what about using memset() instead, it's far more efficient as it uses LDIR.

ronaldo

@gerald: Yes, memset, and various cpct_memset functions are far more efficient (orders of magnitude). This is just kind of an exercise for @mr_lou to learn about pointers, types and memory. Then he can continue with optimization, as you suggest :) .

mr_lou

@ronaldo

How about adding these functions:

void cpct_scrollAreaUpM1 (void* memory, u8 width, u8 height);
void cpct_scrollAreaDownM1 (void* memory, u8 width, u8 height);
void cpct_scrollAreaLeftM1 (void* memory, u8 width, u8 height);
void cpct_scrollAreaRightM1 (void* memory, u8 width, u8 height);

Scrolls everything in the area 1 pixel in the direction.  :)


FloppySoftware

Quote from: ronaldo on 18:00, 13 July 15
Anyways, your previous code, even having a typing confusion, should work well. However, SDCC seems to do something quite bizarre:

;src/main.c:41: for (addr = 0xc000; addr < 0xffff; addr++) {
        ld      de,#0x3FFF
00107$:
;src/main.c:42: *((u8*) addr) = 255;
        dec     de
        ld      l, e
        ld      h, d
        ld      (hl),#0xFF
        ld      h,e
        ld      l,d
        ld      e, h
;src/main.c:41: for (addr = 0xc000; addr < 0xffff; addr++) {
        ld      a,l
        ld      d,a
        or      a,h
        jr      NZ,00107$

It initializes DE to 0x3FFF and goes decreasing it, copying it to HL and using HL to write FF to memory. It effectively deletes memory from 0x3FFF to 0x0000, which makes no sense at all.

To me this seems like an SDCC bug, kind of a confusion with types.

0xFFFF - 0xC000 = 0x3FFF
That kind of bugs are sooooooo nice, because are soooooo easy to find them....!
:laugh: ;D :D ;)
floppysoftware.es < NEW URL!!!
cpm-connections.blogspot.com.es

ronaldo

@mr_lou: Software scrolling functions are already part of CPCtelera's task list. They are not main priority right now, but they should be implemented for future 2.0. release :) .

I will add a note on this 4 functions as your proposal and will consider them when implementing software scroll :) . Right now, you can implement software scroll using cpct_memcpy, as I suggest in my answer to your question in the other thread. It is not the best solution, but it does the job for some cases :) .

mr_lou

I have become rather interested in CPCtelera.
I suspect I might begin working on a 2nd game after this. An idea I've had for years, and tried getting people interested in some years back too.

Even in its current state CPCtelera already feels like a solid framework.

My girlfriend and I have made 4 mobile games. The first two both took us 3 months to make. The 3rd one took us 1½ years. The last one took 3 years (but that was because we became parents in the middle of it).
And now we've spent about 2 weeks with CPCtelera developing this CPC game, and we're almost done already! Granted, it's a simple game, but so was our first mobile game.

More functions will naturally always be desired in CPCtelera. Like e.g. the ability to move a sprite only 1 pixel left and right. (Any plans on adding that?)

I'm curious to know what future functionality you have planned.
Are you still excited to work on CPCtelera?

ronaldo

Quote
I'm curious to know what future functionality you have planned.
Are you still excited to work on CPCtelera?
Yes, of course I am :) . I'm more interested as I see it is useful for people. I started CPCtelera after testing all other libraries and frameworks I knew of, and thought that something different was required. Mainly, I missed good documentation, examples and simplicity . Those were main reasons for starting.

I have lot of plans for CPCtelera: from some easy functions for dealing with tilemaps to complex rasters, screen splitting, overscan modes and other things. However, it will go slow. Next 3 months I will be very busy and will have to stop Z80 coding almost completely. On returning, I will have a pace similar to now: I want to go slow and have the best possible code, with lot of comments and full of documentation. I also would love to have time to record some videos about programming.

Too many things. I have a great task list and lots of ambitious ideas, but want to continue slow and firm :) .

mr_lou

Definitely worth donating to.
Development of CPCtelera will result in more new games.
I'm looking forward to see new functionality in the future!  :)

People might not make it this time, but I suspect CPCtelera will be the direct cause for an overload of submissions for CPCRetroDev2016.  :)

arnoldemu

Re the loop.

Don't assume it's a bug.

Compilers will be set to "signed" or "unsigned" by default.

both integers and characters can be treated like this. Sometimes you can define it with a compile time parameter.

We know what we want, compilers don't always.

In the case above, try this:


for (addr=(u16)0xc000; addr<(u16)0xffff; addr++)
{
}


Concerning pointers:

* they are variables
* the contain the address of data (in basics poke you do this: poke &4000,&ff. &4000 is the address and &ff is the data).
In pointer code you can do this:


u8 *ptr = 0x04000;
ptr[0] = 0x0ff;


OR


u8 *ptr = 0x04000
*ptr = 0x0ff;


The code showed a cast (where a type is changed into another type). Be careful with those.

You can use a pointer as if it was an array:


ptr[1] =
ptr[2] =


If ptr is u8 type, then each element of the array is treated as u8 (or 1 byte), so ptr[1] = ptr+1 and ptr[2] = ptr+2.
If ptr is a u16 type, then each element is 2 bytes (u16=2 bytes). so ptr[1] = ptr + 2, ptr[2] = ptr+4.

address = ptr + element_size*element_index.

Pointers are very powerful.

In C, use pointers to pass structures to avoid them being copied into the function.

If you don't use pointers and "pass by value", then it makes a copy of the data or puts it onto the stack, both wasting space and cpu time.
Use pointers and it will only copy the pointer itself which is 2 bytes, and is the same as a native register size so quick.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ronaldo

Quote from: arnoldemu on 09:44, 14 July 15
Don't assume it's a bug.
Compilers will be set to "signed" or "unsigned" by default.
both integers and characters can be treated like this. Sometimes you can define it with a compile time parameter.
We know what we want, compilers don't always.
In the case above, try this:

for (addr=(u16)0xc000; addr<(u16)0xffff; addr++) {
}

Yes, you are right in that I shouldn't have assumed it is a bug. I didn't think of the possibility of the compiler threating direct constants as signed instead of unsigned, or even not casting them on assignments or comparisons.

However, I have tried using your explicit casting proposal, and the resulting code is the same. I'm thinking of openning a issue for SDCC developers to check it.

gerald

Quote from: ronaldo on 10:39, 14 July 15
Yes, you are right in that I shouldn't have assumed it is a bug. I didn't think of the possibility of the compiler threating direct constants as signed instead of unsigned, or even not casting them on assignments or comparisons.

However, I have tried using your explicit casting proposal, and the resulting code is the same. I'm thinking of openning a issue for SDCC developers to check it.
Which version of SDCC are you using ? I cannot reproduce the problem with SDCC 3.4.0 and the simple test function :

void test(void)
{
  char *addr;
  for (addr =  0xc000 ; addr < 0xffff; addr++)
    {
      *addr = 255;
    }   
}


Compiled code :

;    ---------------------------------
; Function test
; ---------------------------------
_test_start::
_test:
;test.c:6: for (addr =  0xc000 ; addr < 0xffff; addr++)
    ld    hl,#0xC000
00102$:
;test.c:8: *addr = 255;
    ld    (hl),#0xFF
;test.c:6: for (addr =  0xc000 ; addr < 0xffff; addr++)
    inc    hl
    ld    d,l
    ld    e,h
    ld    a,d
    sub    a, #0xFF
    ld    a,e
    sbc    a, #0xFF
    jr    C,00102$
    ret
_test_end::



Note also that the proper cast should be :

void test(void)
{
  char *addr;
  for (addr =  (char *) 0xc000U ; addr < char *) 0xffffU; addr++)
    {
      *addr = 255;
    }   
}


ronaldo

@gerald: you are not using the same type. The program works well when using a byte pointer (char* or unsigned char*). The problem shows when using an unsigned int (u16) to store the address.

SDCC is 3.5.0, latest one.

Note that u8 and u16 are typedefs included with CPCtelera:

typedef unsigned char u8;
typedef unsigned int  u16;

FloppySoftware

Humm... in the original code, addr it's not declared as a pointer but a simple  unsigned int:

u16 addr;

I think that is the reason that causes SDCC to out that code.

For SDCC it's a loop with a well known amount of iterations (just a subtract with constant values).

Of course, this can be a bad optimization decision, but having in mind that it is not very standard to use unsigned ints as pointers...
floppysoftware.es < NEW URL!!!
cpm-connections.blogspot.com.es

ronaldo

@FloppySoftware: SDCC shouldn't change the value of addr, being it used inside the loop. It doesn't matter if it knows the iterations. However, using addr through a cast ( *(u8*)addr) ) may be fooling SDCC into thinking that the value of addr is not used. That may be a reason for it changing into the number of iterations to use a jr nz, which is faster than a comparison and a jump.

Under this analysis I see it more clear as a bug due to an incorrect optimization assumption.

FloppySoftware

Yes, but the use of unsigned ints as pointers it's not very standard, as you know.

I think it's better to use a pointer. That's standard.
floppysoftware.es < NEW URL!!!
cpm-connections.blogspot.com.es

ronaldo

@FloppySoftware: Absolutely. A pointer should be used to point to values, no doubt about that.

The problem here is not that we wanted to code using unsigned ints as pointers, the problem is that we came to this by chance, and seems that SDCC has a bug. An unsigned int value shoud have no problem when converted to a byte pointer, as it is a 16-bits value. However, due to optimization assumptions, the internal value of the variable is different from the one the programmer wanted it to be.

Powered by SMFPacks Menu Editor Mod