• Order of the Butterfly
    Order of the Butterfly
    BalrogSoft
    Posts: 171 from 2006/10/6
    From: Spain
    I will publish some of my code to develop 4k intro for our favourite operative system, Windows 8... eheemm, no, seriously, for our favourite system, MorphOS.

    For this guide, we will need MorphOS SDK, and lzma_loader to crunch the executable output.

    MorphOS SDK
    Lzma Loader

    Develop a 4k intro can be divided on 4 tasks:

    Statup code.
    Graphics code.
    Audio code.
    Intro itself.


    And we will add an extra task, a stack virtual machine.

    Startup code

    Some time ago I published some code to do this task, I have received help from this forum to optimize this part, specially from Piru.

    This code is based on that startup code published, I have made small changes but basically the tasks are:

    Initialize OpenGL Context.
    Initialize AHI device.
    Calculate sinus and cosinus tables.


    So here is the startup code:

    Code:

    /*
    * 4Kb Intro startup by Pedro Gil (Balrog Soft)
    * www.amigaskool.net
    *
    * TinyGL and AHI device startup code,
    * and music synth for 4k intros.
    * The song was ripped from howagen intro by tonic.
    *
    * Special thanks to Piru for his small opts,
    * and Morphzone.org people!
    *
    */

    #include <stdio.h>
    #include <limits.h>

    #include <devices/ahi.h>

    #include <proto/exec.h>
    #include <proto/dos.h>
    #include <proto/tinygl.h>
    #include <proto/ahi.h>


    #define ubyte unsigned char
    #define uint unsigned int

    #define byte signed char

    #ifndef M_PI
    #define M_PI 3.14159265
    #define M_PI2 6.283185
    #endif

    // Sinus and cosinus arrays have 720 elements

    #define TABLE_DEGREES 720
    #define DEGREE_STEP M_PI2 / TABLE_DEGREES

    // Define our ahi device for 8 bits
    // signed mono buffer with a frequency
    // of 8000 samples

    #define FREQUENCY 8000
    #define TYPE AHIST_M8S
    #define BUFFERSIZE 24000

    static void display(void);
    static void keys(unsigned char c, int x, int y);
    static void synth4k(void);


    struct ExecBase *SysBase;
    struct Library *TinyGLBase;


    GLContext *__tglContext;

    static struct MsgPort *AHImp = NULL;
    static struct AHIRequest *AHIios1 = NULL;
    static struct AHIRequest *AHIios2 = NULL;
    static struct AHIRequest *link = NULL;

    static struct Message *wbstartupmsg = NULL;
    static struct Process *self = NULL;

    static void *tmp;
    static BOOL init = TRUE;

    static byte AHIDevice;

    // Here are defined two audio buffers,
    // it is similar to a screen double buffer
    static byte buffer1[BUFFERSIZE], buffer2[BUFFERSIZE];
    static byte *p1=buffer1, *p2=buffer2;
    static uint signals;
    static int t, i, j, n;

    // Our sinus and cosinus tables
    static float sinus[TABLE_DEGREES], cosinus[TABLE_DEGREES],
    a = 0, b = 1, c, d = DEGREE_STEP;

    #define __TEXTSECTION__ __attribute__((section(".text")))


    #ifdef NOSTARTUPFILES
    int entry(void)
    #else
    int main(void)
    #endif
    {
    SysBase = *(struct ExecBase **) 4;

    wbstartupmsg = 0;
    self = (struct Process *) FindTask(0);
    if (self->pr_CLI == 0)
    {
    WaitPort(&self->pr_MsgPort);
    wbstartupmsg = GetMsg(&self->pr_MsgPort);
    }

    TinyGLBase = OpenLibrary("tinygl.library", 50);

    if (TinyGLBase)
    {

    // Initialize AHI device

    AHImp=CreateMsgPort();
    #ifndef REMOVE
    if(AHImp != NULL)
    {
    #endif
    AHIios1 = (struct AHIRequest *) CreateIORequest(AHImp, sizeof(struct AHIRequest));
    AHIios1->ahir_Version = 4;
    AHIDevice = OpenDevice(AHINAME, 0, (struct IORequest *)AHIios1, 0);
    #ifndef REMOVE
    }
    #endif

    // Make a copy of the request (for double buffering)

    AHIios2 = AllocVec(sizeof(struct AHIRequest), MEMF_ANY);
    CopyMem(AHIios1, AHIios2, sizeof(struct AHIRequest));

    // Generate sinus and cosinus tables
    i = TABLE_DEGREES;
    while (i)
    {
    c = a + b * d;
    b = b - c * d;
    sinus [TABLE_DEGREES-i] = c;
    cosinus[TABLE_DEGREES-i] = b;
    a = c;
    i--;
    }
    c = 0;

    __tglContext = GLInit();
    if (__tglContext)
    {
    // Initialize TinyGL

    glutInit(NULL,NULL);

    glutInitDisplayMode(GLUT_RGBA);

    #ifndef REMOVE
    glutInitWindowSize(640, 480);
    #endif

    glutFullScreen();

    glutCreateWindow(NULL);

    #ifndef REMOVE
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    #endif

    // Setting blending settings

    glEnable(GL_BLEND); // Enable Blending

    glBlendFunc(GL_SRC_ALPHA, GL_ONE);

    glEnableClientState(GL_VERTEX_ARRAY);

    // Setting projection, P2D define a
    // 2D projection for 2d graphics.
    glMatrixMode(GL_PROJECTION);

    #ifdef P2D
    glOrtho(320.0f, -320.0f,
    240.0f, -240.0f,
    -1.0f, 1.0f);
    #else
    gluPerspective(80, 1.333333f, 1.0, 500.0);
    glTranslatef(0.0f, 0.0f, -240.0f);
    #endif

    glMatrixMode(GL_MODELVIEW);

    // Some unused stuff

    //glScalef(2.0f,2.0f,0.0f);
    //glEnable(GL_LINE_SMOOTH);

    //glHint(TGL_CORRECT_NORMALS_HINT, GL_FASTEST);
    //glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

    // Define our main key and idle functions

    glutKeyboardFunc(keys);

    glutIdleFunc(display);

    // And enter to the main loop.

    glutMainLoop();

    // Close TinyGL Context
    GLClose(__tglContext);
    }

    // Abort any pending AHI iorequests
    AbortIO((struct IORequest *) AHIios1);
    WaitIO((struct IORequest *) AHIios1);

    if(link) // Only if the second request was started
    {
    AbortIO((struct IORequest *) AHIios2);
    WaitIO((struct IORequest *) AHIios2);
    }

    if(!AHIDevice)
    CloseDevice((struct IORequest *)AHIios1);

    DeleteIORequest((struct IORequest *)AHIios1);
    FreeVec(AHIios2);

    // Close TinyGL lib
    CloseLibrary(TinyGLBase);
    }

    if (wbstartupmsg)
    {
    Forbid();
    ReplyMsg(wbstartupmsg);
    }

    return 0;
    }


    static void keys(unsigned char c, int x, int y)
    {
    // Send a fake CTRL+C signal to exit
    if (c == 0x1b)
    {
    SetSignal(SIGBREAKF_CTRL_C, SIGBREAKF_CTRL_C);
    }
    }

    static void display(void)
    {
    // Ahi device handle code

    // Send io request to the device
    if ((signals & (1L << AHImp->mp_SigBit) || init))
    {
    if (init && link)
    init = FALSE;
    if (!link)
    synth4k();
    // AHIios1->ahir_Std.io_Message.mn_Node.ln_Pri = 0;
    AHIios1->ahir_Std.io_Command = CMD_WRITE;
    AHIios1->ahir_Std.io_Data = p1;
    AHIios1->ahir_Std.io_Length = BUFFERSIZE;
    // AHIios1->ahir_Std.io_Offset = 0;
    AHIios1->ahir_Frequency = FREQUENCY;
    AHIios1->ahir_Type = TYPE;
    AHIios1->ahir_Volume = 0x10000; // Full volume
    AHIios1->ahir_Position = 0x8000; // Centered
    AHIios1->ahir_Link = link;
    SendIO((struct IORequest *) AHIios1);
    }

    // Check if there are any signals from ahi device
    if(link)
    {
    // Send a fake signal to not stop the loop
    SetSignal(SIGBREAKF_CTRL_D, SIGBREAKF_CTRL_D);
    signals = Wait(SIGBREAKF_CTRL_D | (1L << AHImp->mp_SigBit));
    }

    link = AHIios1;

    // Swap ahi buffers
    if ((signals & (1L << AHImp->mp_SigBit)) || init)
    {
    if (!init)
    WaitIO((struct IORequest *) AHIios2);

    // Swap buffer and request pointers, and restart
    tmp = p1;
    p1 = p2;
    p2 = tmp;

    synth4k();

    tmp = AHIios1;
    AHIios1 = AHIios2;
    AHIios2 = tmp;
    }

    // Here goes the drawing stuff!

    glClear(GL_COLOR_BUFFER_BIT); // Clear Screen Buffer


    glutSwapBuffers();

    }

    // This is the future 4k synth
    static void synth4k(void)
    {

    }

    #ifdef NOSTARTUPFILES
    /* __abox__ symbol is required or else the binary is loaded as PowerUP app */
    const int __abox__ __TEXTSECTION__ = 1;
    #endif


    entry function does all the initizalization stuff and start the main loop.
    keys function only check if escape key is pressed and send a signal to exit.
    display function does all graphic stuff and also swaps ahi buffers if need.
    synth4k is reserved to fill the next audio buffer with our song.

    To build this code, you will need a makefile, don't you?

    Code:

    CC = gcc
    CPP = g++
    LD = ld
    STRIP = strip
    CFLAGS = -O1 -s -fomit-frame-pointer -noixemul -ISDK:tinygl-sdk/include

    NOSTARTUPFILES = 1
    REMOVE = 1
    2D_PROJECTION = 1

    ifdef NOSTARTUPFILES
    CFLAGS+= -DNOSTARTUPFILES -nostartfiles
    endif

    ifdef REMOVE
    CFLAGS+= -DREMOVE
    endif

    ifdef 2D_PROJECTION
    CFLAGS+= -DP2D
    endif

    INTRO_OBJS = intro.o

    all: intro

    intro: $(INTRO_OBJS)
    $(CC) $(CFLAGS) -o $@ $(INTRO_OBJS)
    $(STRIP) -s -R .comment -R .gnu.version -R .gnu.version_d -R .gnu.version_r $@
    chmod u+x $@

    intro_map:
    $(CC) $(CFLAGS) intro.c -Wl,-Map=foobar.map,--traditional-format

    intro_asm:
    $(CC) $(CFLAGS) -save-temps -o $@ $(INTRO_OBJS)

    clean:
    rm -f *.o intro


    Ok, but this executable is sooo big! It's near 4k limit, what to do? lzma loader will save your butt, again Piru made an excellent job, and I obtain an executable of 1660 bytes, maybe too much for a startup code, but it's not so bad.

    You can put lzma loader files inside your build directory and use this script to get your intro crunched on ram disk.

    Code:

    copy lzmaLoader_shared ram:intro
    lzma e intro -so >> ram:intro


    You have it, a startup code for 4k intros, but where is the fun? a black screen and no sound? On the next episode, beloved childrens, we will add a 4k synth with oscillators, envelope, tracks and patterns support, and all of this for only 1kb! enough to make a sweeeet chip tune!

    For lazy people like me, you can download all the files here:

    http://www.amigaskool.net/download/4k_intro_1.lha

    [ Edited by BalrogSoft 18.05.2015 - 21:49 ]
    Balrog Software - AmigaSkool.net
    Mac Mini - MorphOS 3.8 - G4 1.5Ghz - Ati 9200 64 Mb
    Efika - MorphOS 3.6 - Ati 9200 64Mb - 80 Gb HD
    Amiga 1200D - OS 3.9 - Blizzard 603e/240mh
  • »13.05.15 - 23:14
    Profile Visit Website