SDCC for the Z80

michaelh@juju.net.nz
$Id: sdcc.html,v 1.1.1.1 2005/03/15 06:05:14 michaelh Exp $

News | Snapshots | Last release | Bug tracking | Links

The idea is to re-target SDCC, a free GPL'ed retargettable C compiler designed for embedded systems to the Zilog Z80 and now the Nintendo Gameboy.

The goals are:

  1. To re-target.
  2. To make sdcc use native utilities where possible.
  3. To make sdcc easier to re-target.
  4. To comment the internal workings.

sdcc recently moved to sourceforge.net. The z80 port is now part of the standard code base, which is available via anonymous cvs from cvs.sdcc.sourceforge.net. Have a look at the Sourceforge documentation for more information. Weekly snapshots are available here. Note that they're untested and may not build. This is a very simple Java based Z80 emulator which I'm using to test the generated code.

News / status:

External links:

20th Nov 99: It can currently compile:

#include "debug.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "fs_internal.h"
#include "fs.h"
#include "errorno.h"

static BYTE *_disk;

#define DEBUG_INODE_SEEK	0

#define MAX_MMAPED_SECTORS	3

/** Used to make the emulation more true to life */
static INT8 _sectors_mmaped;

static BOOLEAN _in_sector_allocate;

/** Map a whole sector into memory.
    Note that there is a limit to how many sectors can be in memory at once.

    @param sector    	Absolute sector to mmap.
    @return 		Pointer to the start of the sector in memory.	
*/
void *sector_mmap(SECTOR sector)
{
    assert(sector<NUM_SECTORS);
    assert(!((sector == 0) && (_in_sector_allocate == FALSE)));
    if (_sectors_mmaped == MAX_MMAPED_SECTORS) {
	assert(0);
	return NULL;
    }
    _sectors_mmaped++;
    return _disk + (sector*SECTOR_LENGTH);
}

/** Release a previosally mmap'd sector.

    @param sector	Pointer returned from sector_mmap()
    @param fIsDirty	TRUE if the sector was modified and should be commited.
    @return		ERR_OK on success
*/
ERROR sector_release(void *pSector, BOOLEAN fIsDirty)
{
    _sectors_mmaped--;
    assert(_sectors_mmaped>=0);
    return ERR_OK;
}

/** Allocate a new sector from the free pool.
    Allocates sequentially.

    @return		The sector number of the just allocated sector
    			or INVALID_SECTOR on disk full.
*/
SECTOR sector_allocate(void)
{
    /* Search the sector allocation table, and return the first one free */
    /* PENDING: just so happens that the SAT is one sector long. Extend */
    BYTE *pSector, *pWalk;
    BYTE bLeft = 0;
    SECTOR sector = 0;

    _in_sector_allocate = TRUE;
    if ((pSector = sector_mmap(SAT_SECTOR))) {
	pWalk = pSector;
	/* Search */
	do {
	    if (*pWalk != 0xFF) {
		BYTE bBits = *pWalk;
		/* Found a free sector - get it's bit */
		while (bBits) {
		    sector++;
		    bBits>>=1;
		}
		/* Set the bit in the SAT */
		*pWalk |= (1<<(sector&7));
		sector_release(pSector, TRUE);
		_in_sector_allocate = FALSE;
		return sector;
	    }
	    sector+=8;
	    pWalk++;
	} while (--bLeft);
	/* No room */
	_in_sector_allocate = FALSE;
	sector_release(pSector, FALSE);
    }
    assert(0);
    return INVALID_SECTOR;
}

/** Seek to an offset within a handle.

    Modes: 
    FS_SEEK_SET - iOffset is absolute from the start of the file.  
    FS_SEEK_CUR - iOffset is relative to the current position.
    FS_SEEK_END - iOffset is relative to the end of the file.

    @param pHandle	The handle to seek in.
    @param iOffset    	The offset to use.
    @param bMode	The seek mode FS_SEEK_*
*/	
ERROR inode_seek(INODE_HANDLE *pHandle, INT16 iOffset, BYTE bMode)
{
    INODE_HEADER *pHeader;
    FS_LENGTH absPos;
    assert(pHandle);

    switch (bMode) {
    case FS_SEEK_SET:
	absPos = iOffset;
	break;
    case FS_SEEK_CUR:
	absPos = pHandle->abs_offset + iOffset;
	break;
    case FS_SEEK_END:
	absPos = pHandle->length + iOffset;
	break;
    default:
	assert(0);
    }

    pHandle->abs_offset = absPos;

#if DEBUG_INODE_SEEK
    dprintf("inode_seek: seeking to %u on %u\n", absPos, pHandle->root_sector);
#endif
    if ((pHeader = sector_mmap(pHandle->root_sector))) {
	if (pHeader->length < absPos) {
	    sector_release(pHeader, FALSE);
	    return ERR_FILE;
	}
	absPos += sizeof(INODE_HEADER);
	pHandle->sector_offset = absPos&(SECTOR_LENGTH-1);
	/* Note that when absPos == file length, these calculations
	   are invalid. */
	if (absPos < SECTOR_LENGTH) {
	    /* Inside the first sector */
	    pHandle->current_sector = pHandle->root_sector;
	}
	else if (absPos < SECTOR_LENGTH*NUM_SECTOR_ENTRIES) {
	    pHandle->current_sector = pHeader->sectors[(absPos/SECTOR_LENGTH)-1];
	}
	else {
	    SECTOR *pSectors;
	    SECTOR sector;
	    sector = pHeader->extended;
	    sector_release(pHeader, FALSE);
	    if ((pSectors = sector_mmap(sector))) {
		pHandle->current_sector = pSectors[(absPos>>SECTOR_LENGTH_EXP)-NUM_SECTOR_ENTRIES];
		sector_release(pSectors, FALSE);
		return ERR_OK;
	    }
	    else {
		assert(0);
		return ERR_FILE;
	    }
	}
	sector_release(pHandle, FALSE);
	return ERR_OK;
    }
    return ERR_FILE;
}