Tight memory map with IM2 table

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
User avatar
jorgegv
Member
Posts: 35
Joined: Wed Nov 18, 2020 5:08 pm

Tight memory map with IM2 table

Post by jorgegv »

Hi guys,

I'm trying to squeeze my memory map as much as I can into low memory. For my setup I can assume the following:
  • I'll never return to basic
  • I setup my own IM2 routine
This means (I think) that I don't need the SYSVARS area for anything and thus I can use it for my purposes in my designed memory map. Which looks like this (abbreviated):

Code: Select all

$4000 - $5AFF -> SCREEN$ (as usual)
$5B00 - $5C00 -> My IM2 table goes here - IM2 vector is $5B, and I load it with 257 x $5C values
$5C5C - $5C5E ->  JP <isr_routine> - I patch the JP opcode and the ISR address here
$5C5F - $5CFF -> Stack (162 bytes) - The stack ptr at start is then set to $5D00
$5D00 - up to SP1 data structs) - The C program
I presume that with this setup the stack and interrupt are set to low memory, and way off the upper 16 KB RAM page ($C000-$FFFF)

So for this setup I write the following code:

Code: Select all

// zcc +zx -vn -SO3 -m -compiler=sdcc -clib=sdcc_iy --max-allocs-per-node200000 main.c -o main.bin -startup=31 -create-app

#pragma output CRT_ORG_CODE           = 0x5d00  // org of compile
#pragma output REGISTER_SP            = 0x5d00  // just below code
#pragma output CRT_STACK_SIZE         = 162     // just enough

#pragma output CRT_ENABLE_CLOSE       = 0       // don't bother closing files on exit
#pragma output CRT_ENABLE_EIDI        = 1       // disable interrupts at start
#pragma output CRT_ENABLE_RESTART     = 1       // do not return to basic

#pragma output CLIB_MALLOC_HEAP_SIZE  = 1024    // heap size
#pragma output CLIB_STDIO_HEAP_SIZE   = 0       // no stdio heap (no files)

#pragma output CLIB_FOPEN_MAX         = -1      // no FILE* list
#pragma output CLIB_OPEN_MAX          = -1      // no fd table

#include <stdlib.h>
#include <im2.h>
#include <string.h>
#include <intrinsic.h>
#include <z80.h>

// data struct and ISR hook for timekeeping
struct time {
   uint8_t hour, min, sec, frame;
} current_time = { 0, 0, 0, 0 };

// timer tick routine
IM2_DEFINE_ISR(do_timer_tick)
{
   if ( ++current_time.frame == 50 ) {          // 50 frames per second
      current_time.frame = 0;
      if ( ++current_time.sec == 60 ) {
         current_time.sec = 0;
         if ( ++current_time.min == 60 ) {
            current_time.min = 0;
            ++current_time.hour;
         }
      }
   }
}

// Initialize interrupts in IM2 mode
#define IV_ADDR         ( (void *) 0x5b00 )
#define ISR_ADDR        ( (void *) 0x5c5c )
#define IV_BYTE         ( 0x5c )
#define Z80_OPCODE_JP   ( 0xc3 )

void init_interrupts(void) {
   intrinsic_di();
   im2_init( IV_ADDR );
   memset( IV_ADDR,IV_BYTE, 257 );
   z80_bpoke( ISR_ADDR, Z80_OPCODE_JP );
   z80_wpoke( ISR_ADDR + 1, (uint16_t) do_timer_tick );
   intrinsic_ei();
}

void main(void) {
   init_interrupts();
   // Just make a small mark at the top of the screen
   *(char*)0x4000 = 0xf2;
   while (1);
}
That code is loaded at address $5D00, which I think gives enough space for the BASIC loader to load and run it (I hope).

But when I compile the above program and run it, It does not show the mark at the screen top, and returns to BASIC. Also the BASIC program is corrupt, since it was not supposed to return.

Can you give any hints about why this is not working?

Thanks
Jorge
User avatar
dom
Well known member
Posts: 1422
Joined: Sun Jul 15, 2007 10:01 pm

Re: Tight memory map with IM2 table

Post by dom »

$5d00/23808 seems a bit low - I think BASIC starts at 23760?

Another issue is that you've pointed im2 into contented memory so you'll get the snow effect.
cborn
Member
Posts: 57
Joined: Tue Oct 06, 2020 7:45 pm

Re: Tight memory map with IM2 table

Post by cborn »

HI, i have half an answer being a starter.
-zorg=32768 -DAMALLOC2
setting the ORG might help perhaps?
are you 100% sure not any lib is using any ROM, .. that uses any sysvar? one of the -startup= does expliciet uses as Much rom as possible, but that might be -startup=0 ?

i did build a IM2 with table on 0x5b00 myself, that works good and is for a 16k model, this link is ASM with some 2 colour border trick
iam very chaotic, so i hope its clear enough te read. i noticed in the z7 the directory is infact double. its at 'inttable' but thats all.
non C here:
https://files.scene.org/view/parties/20 ... oncc16k.7z
cborn
Member
Posts: 57
Joined: Tue Oct 06, 2020 7:45 pm

Re: Tight memory map with IM2 table

Post by cborn »

PS, i avoid using OUT or IN during Screen build, that helps lowering the snow-effect, so only during Border time which might be any thing else then screen.
User avatar
jorgegv
Member
Posts: 35
Joined: Wed Nov 18, 2020 5:08 pm

Re: Tight memory map with IM2 table

Post by jorgegv »

dom wrote: Mon Jun 21, 2021 9:02 pm $5d00/23808 seems a bit low - I think BASIC starts at 23760?
I already thought about this, but I have moved it up in several tries, up to $6000 (and also moved the ISR code and data slightly up, but below code), and still does not work:

Code: Select all

// zcc +zx -vn -SO3 -m -compiler=sdcc -clib=sdcc_iy --max-allocs-per-node200000 main.c -o main.bin -startup=31 -create-app

#pragma output CRT_ORG_CODE           = 0x6000  // org of compile
#pragma output REGISTER_SP            = 0x6000  // just below code
#pragma output CRT_STACK_SIZE         = 200     // just enough

#pragma output CRT_ENABLE_CLOSE       = 0       // don't bother closing files on exit
#pragma output CRT_ENABLE_EIDI        = 1       // disable interrupts at start
#pragma output CRT_ENABLE_RESTART     = 1       // do not return to basic

#pragma output CLIB_MALLOC_HEAP_SIZE  = 1024    // heap size
#pragma output CLIB_STDIO_HEAP_SIZE   = 0       // no stdio heap (no files)

#pragma output CLIB_FOPEN_MAX         = -1      // no FILE* list
#pragma output CLIB_OPEN_MAX          = -1      // no fd table

#include <stdlib.h>
#include <im2.h>
#include <string.h>
#include <intrinsic.h>
#include <z80.h>

uint8_t frame;

// timer tick routine
IM2_DEFINE_ISR(do_timer_tick)
{
   frame++;
}

// Initialize interrupts in IM2 mode
#define IV_ADDR         ( (void *) 0x5e00 )
#define ISR_ADDR        ( (void *) 0x5f5f )
#define IV_BYTE         ( 0x5f )
#define Z80_OPCODE_JP   ( 0xc3 )

void init_interrupts(void) {
   intrinsic_di();
   memset( IV_ADDR,IV_BYTE, 257 );
   z80_bpoke( ISR_ADDR, Z80_OPCODE_JP );
   z80_wpoke( ISR_ADDR + 1, (uint16_t) do_timer_tick );
   im2_init( IV_ADDR );
   intrinsic_ei();
}

void main(void) {
   init_interrupts();
   while(1)
      *(char*)0x4000 = frame;
}
dom wrote: Mon Jun 21, 2021 9:02 pm Another issue is that you've pointed im2 into contented memory so you'll get the snow effect.
Ah, thanks for this. This will make me change my memory layout design, for sure. But I still would like to know why the above does not work....
derekfountain
Member
Posts: 59
Joined: Mon Mar 26, 2018 1:49 pm

Re: Tight memory map with IM2 table

Post by derekfountain »

This is interesting:

Code: Select all

// zcc +zx -vn -compiler=sdcc -clib=sdcc_iy main.c -o main.bin -startup=1 -create-app --list --c-code-in-asm

#include <stdio.h>

void main()
{
  void *val1 = ( (void*)0xf5f5 );
  void *val2 = val1 + 1;

  printf("Val2 is %p\n", val2);
}
On a Spectrum that prints "Val2 is 5F5F". On Linux with GCC:

Code: Select all

>gcc -o main main.c
>./main 
Val2 is 0xf5f6
which is what you'd expect.

The original code is this:

Code: Select all

void init_interrupts(void) {
   intrinsic_di();
   memset( IV_ADDR,IV_BYTE, 257 );
   z80_bpoke( ISR_ADDR, Z80_OPCODE_JP );
   z80_wpoke( ISR_ADDR + 1, (uint16_t) do_timer_tick );
   im2_init( IV_ADDR );
   intrinsic_ei();
}
which compiles (with no optimisation) to:

Code: Select all

593   001F              ;main.c:40: z80_bpoke( ISR_ADDR, Z80_OPCODE_JP );
594   001F  21 5F 5F            ld      hl,0x5f5f
595   0022  36 C3               ld      (hl),0xc3
596   0024              ;main.c:41: z80_wpoke( ISR_ADDR + 1, (uint16_t) do_timer_tick );
597   0024  01 00 00            ld      bc,_do_timer_tick
598   0027  ED 43 5F 5F         ld      (0x5f5f), bc
599   002B              ;main.c:42: im2_init( IV_ADDR );
600   002B  21 00 5E            ld      hl,0x5e00
601   002E  CD 00 00            call    _im2_init_fastcall
602   0031              ;main.c:43: intrinsic_ei();
603   0031  FB                  ei
The bpoke is right, but the +1 on the ISR_ADDR isn't happening, which is breaking the interrupt set up.

This with the most recent z88dk (built from source from today) and whatever couple-of-months-old build I also have on my machine.
User avatar
dom
Well known member
Posts: 1422
Joined: Sun Jul 15, 2007 10:01 pm

Re: Tight memory map with IM2 table

Post by dom »

derekfountain wrote: Tue Jun 22, 2021 10:03 am This is interesting:

Code: Select all

// zcc +zx -vn -compiler=sdcc -clib=sdcc_iy main.c -o main.bin -startup=1 -create-app --list --c-code-in-asm

#include <stdio.h>

void main()
{
  void *val1 = ( (void*)0xf5f5 );
  void *val2 = val1 + 1;

  printf("Val2 is %p\n", val2);
}
On a Spectrum that prints "Val2 is 5F5F". On Linux with GCC:

Code: Select all

>gcc -o main main.c
>./main 
Val2 is 0xf5f6
which is what you'd expect.
Arithmetic with void pointers is a gcc extension I think. FWIW, sccz80 behaves the same as Linux.

Good spot though!
derekfountain
Member
Posts: 59
Joined: Mon Mar 26, 2018 1:49 pm

Re: Tight memory map with IM2 table

Post by derekfountain »

Quite right, but surely it should be flagged with an error rather than generating clearly incorrect code?

The example works if the wpoke() call gets the correct value.
User avatar
dom
Well known member
Posts: 1422
Joined: Sun Jul 15, 2007 10:01 pm

Re: Tight memory map with IM2 table

Post by dom »

derekfountain wrote: Tue Jun 22, 2021 10:34 am Quite right, but surely it should be flagged with an error rather than generating clearly incorrect code?

The example works if the wpoke() call gets the correct value.
It would be nice, but I guess it's the spectre of "undefined behaviour" where anything can happen.

I'll raise an upstream sdcc issue to raise a diagnostic.
Timmy
Well known member
Posts: 218
Joined: Sat Mar 10, 2012 4:18 pm

Re: Tight memory map with IM2 table

Post by Timmy »

dom wrote: Mon Jun 21, 2021 9:02 pm Another issue is that you've pointed im2 into contented memory so you'll get the snow effect.
I think I've already talked about interrupt locations (for 128k and higher models) in some earlier post before: (click on the arrow to see the rest of the post)
Timmy wrote: Wed Feb 10, 2021 12:50 pm Most of sp1 can be left at above 49152. However, the interrupt table and the interrupt routine should be moved to somewhere between 32768-49151. (and the stack should be moved to somewhere safe too.)
User avatar
jorgegv
Member
Posts: 35
Joined: Wed Nov 18, 2020 5:08 pm

Re: Tight memory map with IM2 table

Post by jorgegv »

Thanks a lot, Derek, Dom and Timmy. My issue was exactly with the (void *) pointer arithmetic. I have changed the ISR #defines to be (unsigned char *) pointers, and now everything works fine.

Timmy, thanks for your indications, I'm very much aware of them :-)

This forums are indeed a gold mine, thanks a lot, guys.
J.
User avatar
jorgegv
Member
Posts: 35
Joined: Wed Nov 18, 2020 5:08 pm

Re: Tight memory map with IM2 table

Post by jorgegv »

cborn wrote: Mon Jun 21, 2021 9:17 pm HI, i have half an answer being a starter.
-zorg=32768 -DAMALLOC2
setting the ORG might help perhaps?
are you 100% sure not any lib is using any ROM, .. that uses any sysvar? one of the -startup= does expliciet uses as Much rom as possible, but that might be -startup=0 ?

i did build a IM2 with table on 0x5b00 myself, that works good and is for a 16k model, this link is ASM with some 2 colour border trick
iam very chaotic, so i hope its clear enough te read. i noticed in the z7 the directory is infact double. its at 'inttable' but thats all.
non C here:
https://files.scene.org/view/parties/20 ... oncc16k.7z
Sorry cborn, I forgot to answer you.

My org is set via the CRT_ORG_CODE pragma at the beginning. And the -startup=31 is the one recommended for game development because it is almost completely empty, and it does not use any of the ROM routines.

Thanks also for your help :-)
Post Reply