Shop OBEX P1 Docs P2 Docs Learn Events
Wozasm — Parallax Forums

Wozasm

HollyMinkowskiHollyMinkowski Posts: 1,398
edited 2010-06-13 22:48 in General Discussion
Since reading the iWOZ book I have been getting all
of Wozniac's early asm code I can get hold of and it is all
pretty amazing!

It is in the dialect of an old 8 bit processor (6502) so you
have to get stuff like an opcode list to make any sense of it
but that is all available on the web...use google.

The code is really compact, it's good learning material for
anyone wanting to hone their asm skills.

Here is a totally awesome bit of code he wrote that creates
a software based 16 bit processor with its own opcodes.
Note how tiny this code is! code for a pseudo processor
in only 300 bytes!

********************************
*                              *
*   APPLE-II  PSEUDO MACHINE   *
*         INTERPRETER          *
*                              *
*      COPYRIGHT (C) 1977      *
*     APPLE COMPUTER,  INC     *
*                              *
*     ALL  RIGHTS RESERVED     *
*                              *
*         S. WOZNIAK           *
*                              *
********************************
*                              *
* TITLE:  SWEET 16 INTERPRETER *
*                              *
********************************

R0L     EQU  $0
R0H     EQU  $1
R14H    EQU  $1D
R15L    EQU  $1E
R15H    EQU  $1F
SAVE    EQU  $FF4A
RESTORE EQU  $FF3F

        ORG  $F689

        AST  32

        JSR  SAVE           ;PRESERVE 6502 REG CONTENTS
        PLA
        STA  R15L           ;INIT SWEET16 PC
        PLA                 ;FROM RETURN
        STA  R15H           ;ADDRESS
SW16B   JSR  SW16C          ;INTERPRET AND EXECUTE
        JMP  SW16B          ;ONE SWEET16 INSTR.
SW16C   INC  R15L
        BNE  SW16D          ;INCR SWEET16 PC FOR FETCH
        INC  R15H
SW16D   LDA  >SET           ;COMMON HIGH BYTE FOR ALL ROUTINES
        PHA                 ;PUSH ON STACK FOR RTS
        LDY  $0
        LDA  (R15L),Y       ;FETCH INSTR
        AND  $F             ;MASK REG SPECIFICATION
        ASL                 ;DOUBLE FOR TWO BYTE REGISTERS
        TAX                 ;TO X REG FOR INDEXING
        LSR
        EOR  (R15L),Y       ;NOW HAVE OPCODE
        BEQ  TOBR           ;IF ZERO THEN NON-REG OP
        STX  R14H           ;INDICATE "PRIOR RESULT REG"
        LSR
        LSR                 ;OPCODE*2 TO LSB'S
        LSR
        TAY                 ;TO Y REG FOR INDEXING
        LDA  OPTBL-2,Y      ;LOW ORDER ADR BYTE
        PHA                 ;ONTO STACK
        RTS                 ;GOTO REG-OP ROUTINE
TOBR    INC  R15L
        BNE  TOBR2          ;INCR PC
        INC  R15H
TOBR2   LDA  BRTBL,X        ;LOW ORDER ADR BYTE
        PHA                 ;ONTO STACK FOR NON-REG OP
        LDA  R14H           ;"PRIOR RESULT REG" INDEX
        LSR                 ;PREPARE CARRY FOR BC, BNC.
        RTS                 ;GOTO NON-REG OP ROUTINE
RTNZ    PLA                 ;POP RETURN ADDRESS
        PLA
        JSR  RESTORE        ;RESTORE 6502 REG CONTENTS
        JMP  (R15L)         ;RETURN TO 6502 CODE VIA PC
SETZ    LDA  (R15L),Y       ;HIGH ORDER BYTE OF CONSTANT
        STA  R0H,X
        DEY
        LDA  (R15L),Y       ;LOW ORDER BYTE OF CONSTANT
        STA  R0L,X
        TYA                 ;Y REG CONTAINS 1
        SEC
        ADC  R15L           ;ADD 2 TO PC
        STA  R15L
        BCC  SET2
        INC  R15H
SET2    RTS
OPTBL   DFB  SET-1          ;1X
BRTBL   DFB  RTN-1          ;0
        DFB  LD-1           ;2X
        DFB  BR-1           ;1
        DFB  ST-1           ;3X
        DFB  BNC-1          ;2
        DFB  LDAT-1         ;4X
        DFB  BC-1           ;3
        DFB  STAT-1         ;5X
        DFB  BP-1           ;4
        DFB  LDDAT-1        ;6X
        DFB  BM-1           ;5
        DFB  STDAT-1        ;7X
        DFB  BZ-1           ;6
        DFB  POP-1          ;8X
        DFB  BNZ-1          ;7
        DFB  STPAT-1        ;9X
        DFB  BM1-1          ;8
        DFB  ADD-1          ;AX
        DFB  BNM1-1         ;9
        DFB  SUB-1          ;BX
        DFB  BK-1           ;A
        DFB  POPD-1         ;CX
        DFB  RS-1           ;B
        DFB  CPR-1          ;DX
        DFB  BS-1           ;C
        DFB  INR-1          ;EX
        DFB  NUL-1          ;D
        DFB  DCR-1          ;FX
        DFB  NUL-1          ;E
        DFB  NUL-1          ;UNUSED
        DFB  NUL-1          ;F

* FOLLOWING CODE MUST BE
* CONTAINED ON A SINGLE PAGE!

SET     BPL  SETZ           ;ALWAYS TAKEN
LD      LDA  R0L,X
BK      EQU  *-1
        STA  R0L
        LDA  R0H,X          ;MOVE RX TO R0
        STA  R0H
        RTS
ST      LDA  R0L
        STA  R0L,X          ;MOVE R0 TO RX
        LDA  R0H
        STA  R0H,X
        RTS
STAT    LDA  R0L
STAT2   STA  (R0L,X)        ;STORE BYTE INDIRECT
        LDY  $0
STAT3   STY  R14H           ;INDICATE R0 IS RESULT NEG
INR     INC  R0L,X
        BNE  INR2           ;INCR RX
        INC  R0H,X
INR2    RTS
LDAT    LDA  (R0L,X)        ;LOAD INDIRECT (RX)
        STA  R0L            ;TO R0
        LDY  $0
        STY  R0H            ;ZERO HIGH ORDER R0 BYTE
        BEQ  STAT3          ;ALWAYS TAKEN
POP     LDY  $0             ;HIGH ORDER BYTE = 0
        BEQ  POP2           ;ALWAYS TAKEN
POPD    JSR  DCR            ;DECR RX
        LDA  (R0L,X)        ;POP HIGH ORDER BYTE @RX
        TAY                 ;SAVE IN Y REG
POP2    JSR  DCR            ;DECR RX
        LDA  (R0L,X)        ;LOW ORDER BYTE
        STA  R0L            ;TO R0
        STY  R0H
POP3    LDY  $0             ;INDICATE R0 AS LAST RESULT REG
        STY  R14H
        RTS
LDDAT   JSR  LDAT           ;LOW ORDER BYTE TO R0, INCR RX
        LDA  (R0L,X)        ;HIGH ORDER BYTE TO R0
        STA  R0H
        JMP  INR            ;INCR RX
STDAT   JSR  STAT           ;STORE INDIRECT LOW ORDER
        LDA  R0H            ;BYTE AND INCR RX. THEN
        STA  (R0L,X)        ;STORE HIGH ORDER BYTE.
        JMP  INR            ;INCR RX AND RETURN
STPAT   JSR  DCR            ;DECR RX
        LDA  R0L
        STA  (R0L,X)        ;STORE R0 LOW BYTE @RX
        JMP  POP3           ;INDICATE R0 AS LAST RESULT REG
DCR     LDA  R0L,X
        BNE  DCR2           ;DECR RX
        DEC  R0H,X
DCR2    DEC  R0L,X
        RTS
SUB     LDY  $0             ;RESULT TO R0
        CPR  SEC            ;NOTE Y REG = 13*2 FOR CPR
        LDA  R0L
        SBC  R0L,X
        STA  R0L,Y          ;R0-RX TO RY
        LDA  R0H
        SBC  R0H,X
SUB2    STA  R0H,Y
        TYA                 ;LAST RESULT REG*2
        ADC  $0             ;CARRY TO LSB
        STA  R14H
        RTS
ADD     LDA  R0L
        ADC  R0L,X
        STA  R0L            ;R0+RX TO R0
        LDA  R0H
        ADC  R0H,X
        LDY  $0             ;R0 FOR RESULT
        BEQ  SUB2           ;FINISH ADD
BS      LDA  R15L           ;NOTE X REG IS 12*2!
        JSR  STAT2          ;PUSH LOW PC BYTE VIA R12
        LDA  R15H
        JSR  STAT2          ;PUSH HIGH ORDER PC BYTE
BR      CLC
BNC     BCS  BNC2           ;NO CARRY TEST
BR1     LDA  (R15L),Y       ;DISPLACEMENT BYTE
        BPL  BR2
        DEY
BR2     ADC  R15L           ;ADD TO PC
        STA  R15L
        TYA
        ADC  R15H
        STA  R15H
BNC2    RTS
BC      BCS  BR
        RTS
BP      ASL                 ;DOUBLE RESULT-REG INDEX
        TAX                 ;TO X REG FOR INDEXING
        LDA  R0H,X          ;TEST FOR PLUS
        BPL  BR1            ;BRANCH IF SO
        RTS
BM      ASL                 ;DOUBLE RESULT-REG INDEX
        TAX
        LDA  R0H,X          ;TEST FOR MINUS
        BMI  BR1
        RTS
BZ      ASL                 ;DOUBLE RESULT-REG INDEX
        TAX
        LDA  R0L,X          ;TEST FOR ZERO
        ORA  R0H,X          ;(BOTH BYTES)
        BEQ  BR1            ;BRANCH IF SO
        RTS
BNZ     ASL                 ;DOUBLE RESULT-REG INDEX
        TAX
        LDA  R0L,X          ;TEST FOR NON-ZERO
        ORA  R0H,X          ;(BOTH BYTES)
        BNE  BR1            ;BRANCH IF SO
        RTS
BM1     ASL                 ;DOUBLE RESULT-REG INDEX
        TAX
        LDA  R0L,X          ;CHECK BOTH BYTES
        AND  R0H,X          ;FOR $FF (MINUS 1)
        EOR  $FF
        BEQ  BR1            ;BRANCH IF SO
        RTS
BNM1    ASL                 ;DOUBLE RESULT-REG INDEX
        TAX
        LDA  R0L,X
        AND  R0H,X          ;CHECK BOTH BYTES FOR NO $FF
        EOR  $FF
        BNE  BR1            ;BRANCH IF NOT MINUS 1
NUL     RTS
RS      LDX  $18            ;12*2 FOR R12 AS STACK POINTER
        JSR  DCR            ;DECR STACK POINTER
        LDA  (R0L,X)        ;POP HIGH RETURN ADDRESS TO PC
        STA  R15H
        JSR  DCR            ;SAME FOR LOW ORDER BYTE
        LDA  (R0L,X)
        STA  R15L
        RTS
RTN     JMP  RTNZ




Here are some notes about the pseudo processor.


WOZ said...
SWEET 16: A Pseudo 16 Bit Microprocessor

by Steve Wozniak

Description:

While writing APPLE BASIC for a 6502 microprocessor, I repeatedly encountered a variant of MURPHY'S LAW. Briefly stated, any routine operating on 16-bit data will require at least twice the code that it should. Programs making extensive use of 16-bit pointers (such as compilers, editors, and assemblers) are included in this category. In my case, even the addition of a few double-byte instructions to the 6502 would have only slightly alleviated the problem. What I really needed was a 6502/RCA 1800 hybrid - an abundance of 16-bit registers and excellent pointer capability.

My solution was to implement a non-existant (meta) 16-bit processor in software, interpreter style, which I call SWEET 16.

SWEET 16 is based on sixteen 16-bit registers (R0-15), which are actually 32 memory locations. R0 doubles as the SWEET 16 accumulator (ACC), R15 as the program counter (PC), and R14 as the status register. R13 holds compare instruction results and R12 is the subroutine return stack pointer if SWEET 16 subroutines are used. All other SWEET 16 registers are at the user's unrestricted disposal.

SWEET 16 instructions fall into register and non-register categories. The register ops specify one of the sixteen registers to be used as either a data element or a pointer to data in memory, depending on the specific instruction. For example INR R5 uses R5 as data and ST @R7 uses R7 as a pointer to data in memory. Except for the SET instruction, register ops take one byte of code each. The non-register ops are primarily 6502 style branches with the second byte specifying a +/-127 byte displacement relative to the address of the following instruction. Providing that the prior register op result meets a specified branch condition, the displacement is added to the SWEET 16 PC, effecting a branch.

SWEET 16 is intended as a 6502 enhancement package, not a stand alone processor. A 6502 program switches to SWEET 16 mode with a subroutine call and subsequent code is interpreted as SWEET 16 instructions. The nonregister op RTN returns the user program to 6502 mode after restoring the internal register contents (A, X, Y, P, and S). The following example illustrates how to use SWEET 16.

300  B9 00 02           LDA   IN,Y     ;get a char
303  C9 CD              CMP   #"M"     ;"M" for move
305  D0 09              BNE   NOMOVE   ;No. Skip move
307  20 89 F6           JSR   SW16     ;Yes, call SWEET 16
30A  41         MLOOP   LD    @R1      ;R1 holds source
30B  52                 ST    @R2      ;R2 holds dest. addr.
30C  F3                 DCR   R3       ;Decr. length
30D  07 FB              BNZ   MLOOP    ;Loop until done
30F  00                 RTN            ;Return to 6502 mode.
310  C9 C5      NOMOVE  CMP   #"E"     ;"E" char?
312  D0 13              BEQ   EXIT     ;Yes, exit
314  C8                 INY            ;No, cont.



WOZ said...
SWEET16 contains sixteen internal 16 bit registers, actually the first 32 bytes in main memory, labelled R0 through R15. R0 is defined as the accumulator, R15 as the program counter, and R14 as a status register. R13 stores the result of all COMPARE operations for branch testing. The user acceses SWEET16 with a subroutine call to hexadecimal address F689. Bytes stored after the subroutine call are thereafter interpreted and executed by SWEET16. One of SWEET16's commands returns the user back to 6502 mode, even restoring the original register contents.

Implemented in only 300 bytes of code, SWEET16 has a very simple instruction set tailored to operations such as memory moves and stack manipulation. Most opcodes are only one byte long, but since she runs approximately ten times slower than equivalent 6502 code, SWEET16 should be employed only when code is at a premium or execution is not. As an example of her usefulness, I have estimated that about 1K byte could be weeded out of my 5K byte Apple-II BASIC interpreter with no observable performance degradation by selectively applying SWEET16. [noparse]/noparse

There is an old book full of Wozniac's code snippets.
Nobody seems able to come up with a copy...I'd sure
like a pdf of this thing
index155-2.jpeg

This code was not written for something as different as the Propeller
but the ideas can really make you think. The asm is not that far removed
from something like an 8 bit AVR though.

I wish that this guy would write some PASM code smile.gif

www.6502.org/tutorials/6502opcodes.html

Post Edited (HollyMinkowski) : 6/11/2010 1:49:07 PM GMT

Comments

  • edited 2010-06-11 13:43
    It might be beneficial to contact Woz himself as he is on the net and may be friendly due to his celebrity status.· These people know a little more than they put into print [noparse]:)[/noparse]
  • ratronicratronic Posts: 1,451
    edited 2010-06-11 18:14
    I remember my first contact with 6502 assemby code playing with a Comodore 'Vic 20' back in 1982!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ···································Fix it, if ain't broke!


    D Rat

    Dave Ratcliff N6YEE
  • Rich GreenRich Green Posts: 11
    edited 2010-06-11 19:13
    A lot of early apple II documentation can be found here.

    ftp://public.asimov.net/pub/apple_II/

    Rich Green
  • BradCBradC Posts: 2,601
    edited 2010-06-12 13:42
    The Sweet 16 was cool. I've still got the source in the back of my old Apple ][noparse][[/noparse]+ & ][noparse][[/noparse]e manuals. Wish I remembered who I'd lent my ][noparse][[/noparse]+ to though..

    I missed the elegant simplicity of the 6502 till I found the Prop.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "I mean, if I went around sayin' I was an emperor just because some moistened bint had lobbed a scimitar at me they'd put me away!"
  • potatoheadpotatohead Posts: 10,261
    edited 2010-06-12 16:41
    For me, it's the same. 6502, and 6809 were my faves until the Propeller. Still enjoy the 6809. Anyway, the Apple ][noparse][[/noparse] was a great entry computer. It came with programmers goodies, like the sweet 16 interpreter in the ROM, along with a system monitor and mini one line assembler. These were the tools I used to write my first assembly language code. My first machine code was done in some history class, with an opcode lookup chart, calculating all the branches by hand. Typed the hex codes into the monitor and ran it. Very humbling to work on stuff for a long time, only to have it all be done in a small fraction of a second. The nice thing about the Apples, is the assumption that people were going to be doing stuff like that on the machines.

    IMHO, a card for the Apple ][noparse][[/noparse], containing a Propeller, designed for slot 7, or 0, whichever one of those lets you take over the Apple CPU, would make for a very interesting, though large, propeller dev station. Been wanting to do it actually.

    The 6502 is interesting and fun today because of all the tricks possible. Over time, people have continued to make shorter and or faster code. The same is true for other CPUs too. An example of this on 6809 is moving big blocks of memory using the stack instructions. Set up an address, push a lot of RAM onto the registers, pull it back off at the new address, letting the auto increment do it's thing. Beats the heck out of a load, load, index, store, store loop.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    8x8 color 80 Column NTSC Text Object
    Wondering how to set tile colors in the graphics_demo.spin?
    Safety Tip: Life is as good as YOU think it is!
  • Chris SavageChris Savage Parallax Engineering Posts: 14,406
    edited 2010-06-12 18:36
    Potatohead...my initial experiences were very similar...I started out with the 6502 on both the ][noparse][[/noparse] and C= VIC-20...on the Apple I would program the monthly code that Byte used to print plus a few a couple of really cool games for that time, like Jordan VS Bird, One on one and Conan. I didn't do much else with it. And even though the VIC had almost no memory, it came to me with a Programmer's reference guide and an assembler and I was able to eventually interface to the user port both electrically and programmatically. I got into 6809 when a friend brought over a stack of CoCos he had picked up from a closed down business. Eventually after the C=64 I got into Amiga and did some 68000 programming, but backed off to the Z80 when a small stockpile of them landed in my possesion and I was hooked. Did everything on the Z80 for almost 10 years. Parallax brought me over with the BASIC Stamp and Propeller though...and now I just need some time to actually finish some Propeler based projects. =)

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Chris Savage

    Parallax Engineering
    ·
  • K2K2 Posts: 693
    edited 2010-06-13 01:16
    My first programming experience of any sort was assembly on a 6502. The 6502 certainly lives on for me. A CMOS reincarnation (65C02) is still available in a 40-pin DIP that will typically clock above 20 MHz. I have ambitions of building a FIG FORTH machine with it.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    The early bird gets the worm but the second mouse gets the cheese.
  • SeariderSearider Posts: 290
    edited 2010-06-13 03:51
    I too started on a 6502 doing asm and the forth building custom boards for Oilfield instrumentation systems. I dabbled in 8086 but moved on and it was the prop that got me excited about imbedded systems work again. It’s pretty amazing that so many people went from 6502 to the Prop.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔

    Searider
  • potatoheadpotatohead Posts: 10,261
    edited 2010-06-13 06:43
    There were a LOT of 6502 and Z80 systems. The 6809 saw a little bit of love too, but not in consumer space like those two chips did.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Propeller Wiki: Share the coolness!
    8x8 color 80 Column NTSC Text Object
    Wondering how to set tile colors in the graphics_demo.spin?
    Safety Tip: Life is as good as YOU think it is!
  • K2K2 Posts: 693
    edited 2010-06-13 22:48
    "It’s pretty amazing that so many people went from 6502 to the Prop."

    One thing they share is a dependency on self-modifying code.

    I wasn't given much instruction on the 6502 before my first programming task, and I can still remember the surprise when I found, in the middle of the task, that the only way I could figure to do a particular thing was to code a jump to a dummy location and then go back and rewrite the operand field during runtime. I had never heard the expression "self-modifying code," and I was almost embarrassed about what I thought was just my crazy scheme.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    The early bird gets the worm but the second mouse gets the cheese.
Sign In or Register to comment.