
                    The latest SPEW instruction set for the:

         h u g i   s i z e   c o d i n g   c o m p e t i t i o n   # 9

                                     by TAD
                            Dated: 1st September 1999


        This  text  document  is intended  to  be a more detailed
        description  of  the SPEW cpu  instruction  set and as an
        supplement  to  the main  Hugi Size Coding Competition #9
        rules.  If you require any  further information about the
        competition then please read that document first.

        I  have tried to keep  this very simple, while describing
        all the important bits which could cause you headaches.

                "First make it....
                        then make it smaller..."

                                         the SPEW designers

Changes:- TAD
        - added the (n) order column to the emulation stages




                        A quick overview of the SPEW cpu


        - it has 4Kb (4096 bytes) of r/w memory
        - a 12-bit address bus (bits 15..12 are ignored)
        - two 8-bit registers (A and STATUS)
        - two 16-bit registers (PC and STK)
        - 16 conditional jump instructions
        - 2 arithmetic instructions (ADCA and SBBA)
        - 3 logical instructions (ORA, ANDA, XORA)
        - 2 addressing modes (direct and indirect pointer)
        - memory based registers
        - limited input/output support using INT 21h
        - the normal 80x86 low, high byte order.


Key:


        mem[xxx]                        - a MOD 4096 memory address.

        A       mem[F00]                - 8-bit accumulator
        STATUS  mem[F01]                - status (flags) register
        STK     mem[F02]..mem[F03]      - stack pointer (SP)
        PC      mem[F04]..mem[F05]      - program-counter (IP)

        STATUS <--<-- flags             - pack 80x86 flags into STATUS reg
        flags <--<-- STATUS             - extract 80x86 flags from STATUS

(n)     the number 'n' refers to the order in which an emulation stage
        should be performed in. You can freely swap stages which have
        the same (n) value.



Order of emulation stages:


The order of certain stages in the emulation of a SPEW instruction is
important. I have used the (..) brackets to show the order of these stages.

E.g.

Instruction: "XORA [xxx]"

(1)             PC + 2
(1)             flags <--<-- STATUS
(2)             A XOR mem[xxx]
(3)             STATUS <--<-- flags

The above example shows 3 ordered stages (1) (2) and (3). You can swap
items within the same stage, so this is okay:-

Instruction: "XORA [xxx]"

(1)             flags <--<-- STATUS  ; -- swapped --
(1)             PC + 2               ; -- swapped --
(2)             A XOR mem[xxx]
(3)             STATUS <--<-- flags


but this is NOT okay:-
            ===

Instruction: "GOSUB xxx"

(1)             STK - 2
(2)             WORD mem[STK] = PC
(1)             PC + 2                ;*** PROBLEM ***
(3)             PC = 0xxx

As you can see the "PC + 2"  item MUST be done before "WORD mem[STK] = PC"
otherwise the return-value on the stack (ie. PC+2) is incorrect.



STATUS and the 80x86 FLAGS


The  STATUS register format is identical to the 80x86 FLAGS bits 7..0, except
the VF (overflow flag) must be duplicated into bit 3.

Bits 5  and 1 of the 80x86 FLAGS  register are *undefined* but  in the STATUS
register they MUST be 0 after the "STATUS<--<--flags" translation.


        F E D C B A 9 8 7 6 5 4 3 2 1 0     - 80x86 FLAGS register
        * * * * V * * * S Z - A - P - C
                
                
                Ŀ
                                
                                
                        S Z # A V P # C
                        7 6 5 4 3 2 1 0     - STATUS register


*       - these bits should NOT be modified

#       - these bits MUST be zero in the STATUS reg.
                        x x 0 x x x 0 x

V       - overflow flag
S       - sign flag
Z       - zero flag
A       - Aux. flag
P       - Parity flag
C       - Carry flag

It  IS  possible to use the STATUS register  as a 2nd 8-bit register by doing
this:


        20FF    PUSHB [0FF]
        3F01    POPB [F01]              ; STATUS = 0FF hex

You  will note that FF hex is usually an illegal value, but it will keep this
value  until an OSCALL, ADCA, SBBA, ORA, ANDA, XORA or  any other instruction
which modifies mem[F01] is executed. After one of these instructions has been
decoded  and  executed the STATUS register  is translated back into a "legal"
value and bits 5 and 1 are once again = 0. (by the STATUS<--<--flags).

Eg.

(1)     40FF    LDA [0FF]               ; A = 0FF hex
(2)     5F01    STA [F01]               ; STATUS = A
(3)     4056    LDA [056]               ; A = 056 hex
(4)     2F01    PUSHB [F01]             ; Push STATUS
(5)     3F00    POPB [F00]              ; A = STATUS
                                        ; NOTE: A = 0FF hex
(6)     9666    ADDW [666],A            ; WORD mem[666] + A
(7)     F001    XORA [001]              ; A XOR 001 hex

gives:
        A = FE hex

                 7 6 5 4 3 2 1 0
                 S Z - A V P - C
        STATUS = 1 0 0 ? 0 0 0 0
                            
                       bits 5 and 1 = 0

It  isn't  until  AFTER instruction (7)  that  the STATUS register contains a
valid number. During instructions (3), (4), (5) and (6) it still contains 0FF
hex.


                         The 4Kb (4096 byte) memory-map


In  the  SPEW  cpu there is  4Kb of RAM  (4096 bytes)  which fills the entire
12-bit  address  range.  ALL of these memory  locations  can be read from, or
written into.

ALL addresses are MOD 4096 (i.e. 000..FFF hex) so any address beyond the 4096
byte  limit  will wrap around past 000.  This is true for the STK, PC and the
indirect RDI/WRI instructions when they access memory.  The SPEW cpu only has
a 12-bit address bus, so that no matter what these bits are, the same address
is always accessed.

This 4Kb wrap-around does not affect the 16-bit value. For example if the STK
register  mem[F02..F03] = 0000  and a  PUSHB 4E  instruction occured, the STK
register mem[F02..F03] would = FFFF hex and mem[FFF] = 4E. It is only the mem
address is which MOD 4096, the 16-bit STK register is FFFF hex.

In short, "mem[xxx]" means "mem[ (xxx MOD 4096) ]".  The "mem" prefix implies
a 4Kb memory address wrap-around.


 Address
=========
000..0FF hex    = default values 00..FF hex (reset during boot-up).

100..EFF        = code/data area

F00             = "A" (accumulator) register
F01             = "STATUS" register
F02..F03        = "STK" register (16-bit stack pointer)
F04..F05        = "PC" register (16-bit instruction pointer)

F06..FFF        = default stack area


                Memory wrap-around, Word-access and "Constants"


To  keep  costs down the Sintel Corp (grin)  decided NOT to design any ROM or
hardware   protection.   This  means  ALL  4096  bytes   of  memory  are  R/W
(read/writable).  Upon  boot-up  the SPEW cpu  loads  a  4Kb image and resets
memory  locations mem[000]..mem[0FF] with the numbers 00..FF hex, effectively
turning them into pre-defined values in memory.

Once  this boot-up process is over the SPEW  cpu is free to read/write to ANY
of these 4096 bytes, even mem[000]..mem[0FF].

                  ILLEGAL Operations: (SPEW programmers only)
                  
There  are certain operations which are  illegal. These will NOT occur during
any  of the test-suite programs. They are explained here just for the benefit
of the test-suite programmers.

        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        !!!                                                         !!!
        !!! You emulator does NOT have to detect/handle these cases !!!
        !!!                                                      !!!
        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

It  is  ILLEGAL  for any word access  to  occur  at mem[FFF] because the SPEW
hardware can not handle the wrap-around during a single word operation.

E.g.
        9FFF    ADDW    [FFF], A        ** ILLEGAL **
                                   r/w  mem[FFF]..mem[000]
and this:
                STK = 001 hex

        1xxx    GOSUB   xxx             ** ILLEGAL **
                                   wrt  mem[FFF]..mem[000]

and also this:
                STK = FFF hex

        1000    RETURN                  ** ILLEGAL **
                                   read mem[FFF]..mem[000]

and this:
        0FFF    JP      xxx             ** ILLEGAL **
                                 opcode mem[FFF]..mem[000]


All  of  the  above actions are illegal and  should  NOT  be used in any SPEW
program/test-suite.

The  following instructions ARE valid because  the word access does not cross
the FFF hex limit.

E.g.
        9FFE    ADDW    [FFE], A        okay

                STK = 000 hex
        1xxx    GOSUB   xxx             okay
                                        STK = FFE hex

                STK = FFE hex
        1000    RETURN                  okay
                                        STK = 000 hex

        0FFE    JP      FFE             okay
                                        PC = FFE



                           The SPEW instruction set.


00xx    OSCALL xx               Use an OS call (int 21h service)
0xxx    JP xxx                  Jump to new PC address
1000    RETURN                  Return from a GOSUB call
1xxx    GOSUB xxx               Go (call) a sub-routine
2xxx    PUSHB [xxx]             Push a byte onto the stack
3xxx    POPB [xxx]              Pop a byte from the stack
4xxx    LDA [xxx]               Load A (accumulator) from memory
5xxx    STA [xxx]               Store A into memory
6xxx    RDI [(xxx)]             Read an indirect byte using mem-ptr
7xxx    WRI [(xxx)]             Write an indirect byte using mem-ptr
8xxx    RDSYS [0000:0xxx]       Read byte from system ram[0000:0xxx]
9xxx    ADDW [xxx],A            Add sign-extended A to word variable
Acpp    JPcc +pp                Jump if the condition is true
Bxxx    ADCA [xxx]              A + mem[xxx] + CF
Cxxx    SBBA [xxx]              A - mem[xxx] - CF
Dxxx    ORA  [xxx]              A OR mem[xxx]
Exxx    ANDA [xxx]              A AND mem[xxx]
Fxxx    XORA [xxx]              A XOR mem[xxx]



00xx    OSCALL xx               Operating System CALL

(1)             PC + 2
(1)             ah = xx                 xx = function/service
(1)             al = A                  pass A in both AL and DL registers
(1)             dl = A
(1)             flags <--<-- STATUS     extract 80x86 flags from STATUS
(2)             int 21h
(3)             A = al                  pass back AL register into A
(3)             STATUS <--<-- flags     pass back 80x86 flags into STATUS reg

S Z # A V P # C
7 6 5 4 3 2 1 0         - STATUS register
r r 0 r r r 0 r

        S - Set if the result is negative, otherwise =0
        Z - Set if the result is zero, otherwise = 0
        # - Always cleared
        A - Set if an Aux. carry was generated
        V - Set if an overflow was generated, otherwise = 0
        P - Set if parity is Even, otherwise = 0
        C - Set if a carry was generated, otherwise = 0


This  will  be used to read STDIN  and  write STDOUT characters together with
triggering  the  termination  of  a SPEW  program  using  the  normal program
termination  INT 21h service.  It also allows  other  INT 21h functions to be
used by the test suite(s).

Only  those OSCALL services which require/returns values in AL and/or DL will
be  used,  all  other  INT 21h services  will  NOT  appear in the test suites
because they use other registers. For example function 09, 3Dh, 42h etc...

A list of allowed OSCALL services can be found elsewhere in this document.


0xxx    JP xxx                  Jump to new PC address (ie. JMP)

(1)             PC = 0xxx

The  16-bit  PC register  (mem[F04]..mem[F05])  is loaded  with the new 'xxx'
address from the bits 11..0 of the opcode word. Bits 15..12 are made = 0.


1000    RETURN                  Return from sub-routine (ie. RET)

(1)             PC = mem[STK]
(2)             STK + 2

The 16-bit PC register is loaded with the word value on top of the stack.


1xxx    GOSUB xxx               Goto sub-routine (ie. a CALL)

(1)             PC + 2
(1)             STK - 2
(2)             WORD mem[STK] = PC
(3)             PC = 0xxx

The 16-bit PC register is  pushed onto the stack  and the new 12-bit value is
loaded from bits 11..0 of the opcode word. Bits 15..12 are made = 0.


2xxx    PUSHB xxx               Push memory byte onto the stack

(1)             PC + 2
(1)             STK - 1
(2)             temp = mem[xxx]
(3)             mem[STK] = temp

A byte from memory is pushed onto the top of the stack.

A  "PUSHB [F02]"  will push the lower byte  of  the STK register - 1 onto the
stack, because the "STK-1" occurs BEFORE the actual memory move.

E.g.
                4000    LDA [000]       ; A = 00
                5F02    STA [F02]       ; mem[F02] = A
                2F02    PUSHB [F02]     ; Push mem[F02] low byte of STK
                3F00    POPB [F00]      ; Pop A
gives:
                A = FF hex



3xxx    POPB xxx                Pop memory byte from the stack

(1)             PC + 2
(2)             temp = mem[STK]
(3)             mem[xxx] = temp
(4)             STK + 1

A  "POPB [F02]"  or "POPB [F03]" instruction  will  result  in the byte being
poped also being incremented, due to the "STK + 1" stage.

E.g.
                STK = 0758 hex  ; mem[F02]=58h,  mem[F03]=07 hex

        2F02    PUSHB [0FF]     ; Push FF
        3F02    POPB  [F02]     ; Pop byte [F02]
gives:-
                STK = 800 hex   ; mem[F02]=00h,  mem[F03]=08 hex


A "POPB [F04]" instruction  can be used to make the PC jump to a new location
by replacing the lower 8-bits.

E.g.

Addr:

100 hex 2F06    PUSHB [006]     ; Push 06
102     3F04    POPB  [F04]     ; Pop byte [F04] = bits 7..0 of PC
104     004C ** OSCALL 4C       ;
106     4105    LDA   [105]     ; A = mem[105]

The "OSCALL" instruction will NOT happen. This is because of the "POPB" which
modifies  the  low-byte  of the PC  register,  so  the first two instructions
perform the exact same thing as a "0106 JMP 106" instruction.


4xxx    LDA [xxx]               Load A (accumulator) from memory

(1)             PC + 2
(2)             A = mem[xxx]

E.g.
        43FA    LDA   [3FA]     ; A = mem[3FA]

        4F00    LDA   [F00]     ; A = mem[F00]
                                ; same as A = A

        4F01    LDA   [F01]     ; A = STATUS


5xxx    STA [xxx]               Store A (accumulator) in memory

(1)             PC + 2
(2)             mem[xxx] = A

E.g.
        4002    LDA   [002]     ; A = mem[002]
        5F01    STA   [F01]     ; STATUS = A
gives:
        A = mem[002]
        STATUS = mem[002]


6xxx    RDI [(xxx)]             Read indirect byte from memory pointer

(1)             PC + 2
(2)             addr = mem[xxx]
(3)             A = mem[addr]

The  RDI  and  WRI instruction act  like  indirect-addressing, but instead of
using "A=[DI]" or "A=[SI]" where a register is encoded within the instruction
opcode  (DI  and  SI), a two-byte memory  location  is  treated like a 16-bit
register and used like a memory pointer.

NOTE:
        Just  like the PC and STK registers a 16-bit memory value
        is  read  but only the lowest  12-bits are used to access
        the 4Kb of memory.


7xxx    WRI [(xxx)]             Write indirect byte from memory-pointer

(1)             PC + 2
(2)             addr = mem[xxx]
(3)             mem[addr] = A

This  is  identical to RDI except it  stores  the accumulator "A" into memory
instead of loading it from memory.



8xxx    RDSYS [0000:0xxx]       Read a system byte from segment zero.

(1)             PC + 2
(2)             A =  system memory [0000:0xxx]

This allows the SPEW cpu to examine bytes in the host's ram such as interrupt
vectors  or  operating-system  variables like  the  key shift status, current
video mode etc...


9xxx    ADDW [xxx],A            Add sign-extended A register to word location

(1)             PC + 2
(2)             WORD mem[xxx] + A       ** A is sign-extend to 16-bits **

The STATUS (flags) are NOT updated by this addition.

This  is used to increment/decrement a 16  bit memory location. It allows the
STK,  PC  (and any other)  two-byte memory  location  to be adjusted by the A
register.  The  A register is sign-extended to  give a value range of -128 to
+127 (FF80 to 007F hex)



Acpp    JPcc    +pp             Conditional jump instruction (ie. Jcc)

(1)             PC + 2
(1)             flags <--<-- STATUS
(2)             IF condition 'c' then PC+pp     (pp is sign-extended)

        A0xx    JPOV +pp        jump if overflow
        A1xx    JPNO +pp        jump if no-overflow
        A2xx    JPC  +pp        jump if carry
        A3xx    JPNC +pp        jump if no-carry
        A4xx    JPZ  +pp        jump if zero
        A5xx    JPNZ +pp        jump it non-zero
        A6xx    JPBE +pp        jump if below or equal
        A7xx    JPA  +pp        jump if above
        A8xx    JPS  +pp        jump if sign=1
        A9xx    JPNS +pp        jump if no-sign
        AAxx    JPP  +pp        jump if parity
        ABxx    JPNP +pp        jump if no-parity
        ACxx    JPL  +pp        jump if less than
        ADxx    JPGE +pp        jump if greater or equal
        AExx    JPLE +pp        jump if less or equal
        AFxx    JPG  +pp        jump if greater than

These  conditional  jump instructions are identical  to the normal 80x86 ones
(apart  from  their  JPcc  naming).  The  +pp  value  is  a relative (signed)
displacement  to the PC register. The +pp value is measured from the start of
the  following  instruction.  A  value  of  00  means  jump  to the following
instruction (ie. a NOP).

E.g.
        again:  D000    ORA [000]       ; - 6  clear CF
                BF01    ADCA [F01]      ; - 4  A = A + CF
                A5FA    JPNZ -6         ; - 2  jump if NZ to 'again:'
>                         ; + 0
                                       ; + 2
                                       ; + 4

 the +pp displacement is measure from here.


Bxxx    ADCA [xxx]              Add byte to the accumulator with Carry

(1)             PC + 2
(1)             flags <--<-- STATUS     ; extract 80x86 flags
(2)             A + mem[xxx] + CF
(3)             STATUS <--<-- flags     ; repack 80x86 flags

S Z # A V P # C
7 6 5 4 3 2 1 0         - STATUS register
r r 0 r r r 0 r

        S - Set if the result is negative, otherwise =0
        Z - Set if the result is zero, otherwise = 0
        # - Always cleared
        A - Set if an Aux. carry was generated
        V - Set if an overflow was generated, otherwise = 0
        P - Set if parity is Even, otherwise = 0
        C - Set if a carry was generated, otherwise = 0



Cxxx    SBBA [xxx]              Subtract byte to the accumulator with Borrow

(1)             PC + 2
(1)             flags <--<-- STATUS
(2)             A - mem[xxx] - CF
(3)             STATUS <--<-- flags

S Z # A V P # C
7 6 5 4 3 2 1 0         - STATUS register
r r 0 r r r 0 r

        S - Set if the result is negative, otherwise =0
        Z - Set if the result is zero, otherwise = 0
        # - Always cleared
        A - Set if an Aux. carry was generated
        V - Set if an overflow was generated, otherwise = 0
        P - Set if parity is Even, otherwise = 0
        C - Set if a carry was generated, otherwise = 0



Dxxx    ORA [xxx]               Logical OR byte to the accumulator

(1)             PC + 2
(1)             flags <--<-- STATUS
(2)             A OR mem[xxx]
(3)             STATUS <--<-- flags

S Z # A V P # C
7 6 5 4 3 2 1 0         - STATUS register
r r 0 ? 0 r 0 0

        S - Set if the result is negative, otherwise =0
        Z - Set if the result is zero, otherwise = 0
        # - Always cleared
        A - **** UNDEFINED ****
        V - Always cleared
        P - Set if parity is Even, otherwise = 0
        C - Always cleared



Exxx    ANDA [xxx]              Logical AND byte to the accumulator

(1)             PC + 2
(1)             flags <--<-- STATUS
(2)             A AND mem[xxx]
(3)             STATUS <--<-- flags

S Z # A V P # C
7 6 5 4 3 2 1 0         - STATUS register
r r 0 ? 0 r 0 0

        S - Set if the result is negative, otherwise =0
        Z - Set if the result is zero, otherwise = 0
        # - Always cleared
        A - **** UNDEFINED ****
        V - Always cleared
        P - Set if parity is Even, otherwise = 0
        C - Always cleared



Fxxx    XORA [xxx]              Logical XOR byte to the accumulator

(1)             PC + 2
(1)             flags <--<-- STATUS
(2)             A XOR mem[xxx]
(3)             STATUS <--<-- flags

S Z # A V P # C
7 6 5 4 3 2 1 0         - STATUS register
r r 0 ? 0 r 0 0

        S - Set if the result is negative, otherwise =0
        Z - Set if the result is zero, otherwise = 0
        # - Always cleared
        A - **** UNDEFINED ****
        V - always cleared
        P - Set if parity is Even, otherwise = 0
        C - Always cleared





                         The allowed OSCALL xx services


NOTE:
        This  only concerns the test-suite/SPU image programmers.
        It  gives  details of which INT  21h can be used (ie. all
        those  functions  which  require/return  values  in AL/DL
        registers).

        You  may  safely assume that  only the following services
        will be used in the test-suite(s).


0000    OSCALL 00       - terminate program


0001    OSCALL 01       - get keyboard input
        A = ASCII character


0002    A = char
        OSCALL 02       - display output


0006    A = char (00..FE hex)
        OSCALL 06       - Direct console output

0006    A = FF hex
        OSCALL 06       - Direct console input
        ZF = 1 (no char)
        ZF = 0 (char)
                A = char

0007    OSCALL 07       - Direct console input without echo
        A = char

0008    OSCALL 08       - Direct console input without echo
        A = char

000B    OSCALL 0B       - Check standard input (STDIN) status
        A = FF (char available)
        A = 00 (no char)

000C    A = function number
        OSCALL 0C       - clear keyboard buffer & invoke keyboard function
        A = 00

0019    OSCALL 19       - get current disk drive
        A = drive

004C    A = exit code
        OSCALL 4C       - terminate a process



              That's all folks !!  Good luck with your emulator !