1. This forum section is a read-only archive which contains old newsgroup posts. If you wish to post a query, please do so in one of our main forum sections (here). This way you will get a faster, better response from the members on Motherboard Point.

C cross-compiler for 6800 (yes, you read correctly)

Discussion in 'Embedded' started by CTH, Feb 10, 2014.

  1. CTH

    Mel Wilson Guest

    I see elsewhere that the Cross-32 Meta-Assembler is still available. Back
    when, that was the only assembler I used for 68HC's and PIC16's. It had a
    little basic syntax of its own, mainly for expressions as operands, and
    beyond that the assembly language was totally defined in tables, which you
    could make for yourself if they weren't included in the package. It had
    trouble with many-instructions-per-line formats as used in DSPs, but most
    everything else turned out to be doable.

    If you had differences of opinion with the Cross-32 support, you could fix
    them up in the tables for yourself. E.g. if your architecture had and-
    immediate and or-immediate ops, you might find

    XYZZY_MASK equ 3 ; mask a field in a byte
    ori some_byte, XYZZY_MASK

    would work, where
    andi some_byte,~XYZZY_MASK
    would blow up with the message that you were trying to stuff a 32-bit value
    into an 8-bit field. You would fix it by changing the permitted limits on
    the andi operand from 0..0xFF to -128..255. Support never saw it that way.
    Felt they were protecting programmers from themselves. We had bought a copy
    for clients in Korea, so we had potential code-compatibility problems, so we
    didn't change the tables finally. Coded the work-around
    andi some_byte,0xFF&{~XYZZY_MASK}

    Good times.

    Mel.
     
    Mel Wilson, Feb 14, 2014
    1. Advertisements

  2. CTH

    dp Guest

    I explicitly said "hardware" stack pointer. One can make as many
    stacks as there is memory for on any usable processor :).

    The fundamental difference here is that on an exception or subroutine
    call there is no memory access imposed by the CPU hardware (thus no
    GP register needs to be any different from the rest).

    Sometimes one does not resort to any stack usage indeed; e.g. on a
    missing PTE exception on a 603e derivative core one gets r0-r3 switched
    to an alternative bank (typically used for nothing else) and loads the
    page translation entry in a few cycles (data memory access needed only
    to the page translation table, program memory can be locked in the cache).
    Must have allowed the designers to simplify the core at the (negligible)
    cost of a few bytes of program memory.

    On "normal" exceptions (i.e. pretty much the rest of them) one usually
    does use a stack, e.g. in DPS there are 3 of them maintained (using only
    one GPR). There is the IRQ stack pointer - which is used at the beginning
    of an exception when it will save registers; there is the supervisor SP
    (task unique) to which the system call exception switches after initially
    using the IRQ SP and there is the user SP - also task unique.

    Dimiter
     
    dp, Feb 15, 2014
    1. Advertisements

  3. CTH

    tridac Guest

    It's been a long time, but there were some small forth packages that
    would run on a 1K ram systems like the Kim 1. Loaded from cassete, iirc.
    Correct me if wrong...

    Regards,

    Chris
     
    tridac, Feb 15, 2014
  4. CTH

    tridac Guest

    May be a bit unfair - just at the time I was a died in the wool asm
    programmer
    (there wasn't anything else) and used to getting a lot done with very few
    instructions :)

    Looking at the asm source, it produced what looked to me like very laboured
    and inefficient code, but perhaps that was the state of the art at the time,
    even allowing for the 16 bit requirements. The 6502 in particular was very
    efficient for 16 bit work, having pre and post indexed indirect
    addressing modes.
    You could write a linked list tree traversal program with just a few
    lines of
    asm...

    Regards,

    Chris
     
    tridac, Feb 15, 2014
  5. CTH

    Marven Lee Guest

    I use coroutines to multithread my device drivers and file system
    server in my half working microkernel os. I don't have kernel
    support for threads so use the Libtask library for coroutines.

    Libtask provides tasksleep(rendez), taskwakeup(rendez),
    taskwakeupall(rendez) and taskyield() functions. These act on
    condition variables. With pthreads a mutex is needed in
    conjunction with a condition variable for mutual exclusion.

    Similarly Unix kernels used to disable preemption upon
    entering the kernel and use the same sleep and wakeup
    on condition variable primitives. So really Unix kernels
    acted like a collection of coroutines

    Both my file system and drivers use coroutines and follow
    a similar multiple Directors - single Secretary pattern where
    the main task is the Secretary that listens for incoming events
    using a select()-like system call. The secretary then wakes
    up whatever Director coroutine handles that connection or
    unit to do the actual work.

    The director coroutines may wakeup or sleep on other coroutines
    or wait for futher events from the secretary. When all Director
    coroutines can no longer progress and are sleeping the Secretary
    goes back to waiting on external events using the select()-like
    system call.

    If a director blocks on say an interrupt it will still allow the other
    unit's director to service incoming messages and interrupts.

    In pseudocode it might look like...

    Secretary()
    {
    while (1) {
    handle = WaitFor (ANY);

    if (handle is message port) {
    u = DetermineUnit(handle);
    unit->message_pending = 1;
    taskwakeup (unit->message_rendez);
    }

    if (handle is interrupt) {
    u = DetermineUnit(handle);
    unit->interrupt_pending = 1;
    taskwakeup (unit->interrupt_rendez);
    }

    // taskyield() returns 0 when there are no
    // other coroutines to run.
    while (taskyield() != 0);
    }
    }


    Director()
    {
    while (1) {
    while (this->message_pending == 0)
    tasksleep (this->message_rendez);

    this->message_pending = 0;
    msg = GetMsg (this->msgport_handle);

    // Process message, begin io, wait for interrupt

    while (this->interrupt_pending == 0)
    tasksleep (this->interrupt_rendez);

    this->interrupt_pending = 0;
    UnmaskInterrupt(this->irq);

    PutMsg (this->msgport_handle, reply_msg);
    }
    }


    The file system works in a similar way, with a director coroutine
    for each incoming connection. These directors can be blocked
    if vnodes or cache buffers are marked as busy so they have to
    sleep like the example above. When another director is
    finished with a vnode or buffer it marks them as not busy and
    akes up the other coroutines sleeping on them.

    There are an additional set of coroutines that I call subsecretaries,
    These handle the strategy() function, taking a queue of buffers and
    sending them to the device drivers to be read or written. Once done
    they mark the buffers as not busy and notify any waiting directors.

    There are two other coroutines, one to automount drivers and another
    to perform a periodic sync so that buffers marked as delayed write
    eventually get written. My code for acquiring and waiting for the
    vnodes and cache buffers followed the description in the book
    by Bach called, "The design of the Unix operating system."

    Multithreading with coroutines in this way allows the file system to
    continue serving requests from other processes for data already in
    the cache whilst an IO operation to the device driver is underway.
    It could be considered a form of hit-under-miss.

    The original Minix file system implementation had issues with the
    lack of threading. A read from the floppy would block requests
    from other processes being serviced until the slow floppy IO was
    complete. Someone did implement a multithreaded file system
    for Minix in the early 90s, but I couldn't find anything about it.

    The current Minix added a multithreaded file system in
    the last few years using coroutines I believe. QNX used
    coroutines inside its file system FSys server before they
    implemented kernel threads, they may still use coroutines.
    The Amiga's file system, dos.library also used coroutines.

    The HelenOS operating system is a microkernel and they
    have coroutine support and calls them 'fibrils'. I believe
    they have true kernel threads as well so I'm not sure if
    it uses coroutines inside its file system server.

    I really thought I had discovered something new when
    I worked out how to do cooperative multitasking inside
    a file system a few years ago. I thought I was genius
    who deserved a Turing award for what I discovered.

    That is until I learnt that what I had discovered were called
    coroutines and were a really old technique. I felt a bit
    depressed that I hadn't discovered something new :-(
     
    Marven Lee, Feb 19, 2014
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.