sdcc initial version of paul's xa_asm
authorjohanknol <johanknol@4a8a32a2-be11-0410-ad9d-d568d2c75423>
Sat, 19 Jan 2002 15:43:58 +0000 (15:43 +0000)
committerjohanknol <johanknol@4a8a32a2-be11-0410-ad9d-d568d2c75423>
Sat, 19 Jan 2002 15:43:58 +0000 (15:43 +0000)
git-svn-id: https://sdcc.svn.sourceforge.net/svnroot/sdcc/trunk/sdcc@1819 4a8a32a2-be11-0410-ad9d-d568d2c75423

as/xa51/Makefile [new file with mode: 0644]
as/xa51/bigtest.xa [new file with mode: 0644]
as/xa51/local_label.xa [new file with mode: 0644]
as/xa51/mt.xa [new file with mode: 0644]
as/xa51/paulmon2.xa [new file with mode: 0644]
as/xa51/todo [new file with mode: 0644]
as/xa51/xa_asm.l [new file with mode: 0644]
as/xa51/xa_asm.y [new file with mode: 0644]
as/xa51/xa_dasm.c [new file with mode: 0644]
as/xa51/xa_main.c [new file with mode: 0644]
as/xa51/xa_main.h [new file with mode: 0644]

diff --git a/as/xa51/Makefile b/as/xa51/Makefile
new file mode 100644 (file)
index 0000000..6edcc23
--- /dev/null
@@ -0,0 +1,40 @@
+CC = gcc
+CFLAGS = -O2
+YACC = bison -y -d
+LEX = flex -i
+LEXLIB = 
+
+xa_asm: xa_main.o xa_asm.tab.o xa_asm.lex.o xa_dasm.o
+       $(CC) -o xa_asm xa_main.o xa_asm.tab.o xa_asm.lex.o xa_dasm.o $(LEXLIB)
+       strip xa_asm
+
+xa_asm.lex.o: xa_asm.lex.c xa_asm.tab.h xa_main.h
+       $(CC) $(CFLAGS) -c xa_asm.lex.c
+
+xa_asm.tab.o: xa_asm.tab.c xa_asm.tab.h xa_main.h
+       $(CC) $(CFLAGS) -c xa_asm.tab.c
+
+xa_main.o: xa_main.c xa_main.h
+       $(CC) $(CFLAGS) -Wall -c xa_main.c
+
+xa_dasm.o: xa_dasm.c
+       $(CC) $(CFLAGS) -Wall -c xa_dasm.c
+
+xa_asm.tab.c xa_asm.tab.h: xa_asm.y
+       $(YACC) xa_asm.y
+       mv y.tab.c xa_asm.tab.c
+       mv y.tab.h xa_asm.tab.h
+
+xa_asm.lex.c: xa_asm.l
+       $(LEX) xa_asm.l
+       mv lex.yy.c xa_asm.lex.c
+
+all: touch xa_asm
+
+touch:
+       touch xa_asm.y xa_asm.l xa_dasm.c
+
+clean:
+       rm -f *.tab.c *.tab.h *.lex.c *.o *.bak y.output *.hex *.lst *.obj
+       rm -f xa_asm core *~
+
diff --git a/as/xa51/bigtest.xa b/as/xa51/bigtest.xa
new file mode 100644 (file)
index 0000000..a68df3c
--- /dev/null
@@ -0,0 +1,141 @@
+
+;this is a comment
+
+        .equ    var, 40
+        .bit    my_bit, var.5  ;this is another comment
+
+       add     r5, r1
+       add     r4l, r2h        ;this is the third comment
+       add     r14, r2
+
+       add     [r5], r11
+       add     [r4], r1h
+
+       add     r2, [r0]
+       add     r4l, [r5]
+
+       add     r3, [r1+54]
+       add     r6h, [r5+31]
+
+       add     [r7+3], r2l
+       add     [r6+2], r5
+
+       add     r4, [r2+5405]
+       add     r3h, [r5+-31321]
+
+       add     [r1+1032], r3l
+       add     [r2+-1232], r7
+
+       add     r1, [r6+]
+       add     r3h, [r2+]
+
+       add     [r5+], r2
+       add     [r1+], r7l
+
+       add     341, r3
+       add     345, r1l
+
+       add.w   r6, 31
+       add.b   r5h, 1029
+
+       add     r3l, #54
+       add     r0, #12411
+
+       add.w   [r7], #4223
+       add.w   [r1], #41122
+
+       add.b   [r1+], #45
+       add.b   [r6+], #-10
+
+       add.b   [r2+42], #41
+       add.w   [r1+89], #3112
+
+       add.b   [r7+32321], #12
+       add.w   [r3+8761], #5891
+
+       add.b   1231, #31
+       add.w   156, #9861
+
+       add     r0, r1
+       mov     [r4+4], r6
+loop:  mov     r2, r1
+       asr     r1, #3
+       mov     r1l, #9
+       mov     [r1+4], r2
+       jmp     loop
+       bov     loop
+
+       addc    r15, r4
+       addc    r11, r10
+       addc.b  [r7+4], #-30
+       addc.b  [r3+], #21
+        addc.w  [r7+4], #-30
+        addc.w  [r3+], #321
+
+       adds    r3, #4
+       adds.w  [r1], #6
+       adds.w  [r0+], #-3
+       adds.w  [r5+4], #2
+       adds.w  [r2+-500], #1
+       adds.w  var, #1
+
+        adds    r3h, #4
+        adds.b  [r1], #6
+        adds.b  [r0+], #-3
+t1:     adds.b  [r5+4], #2
+        adds.b  [r2+-500], #1
+        adds.b  var, #1
+
+t2:    and     [r5+3287], r1l
+       and.w   var, #2011
+
+       anl     c, 43.1
+       anl     c, /my_bit
+
+       asl     r3, r1l
+       asl     r11, r5h
+t65:   asl.d   r4, r1h
+       asl.w   r0, r4l
+       asl.b   r4h, r3l
+        asl     r0, r4l
+
+
+       asl.b   r1l, #6
+       asl.b   r3h, #11
+       asl.w   r1, #3
+       asl.w   r4, #7
+        asl     r1l, #6
+        asl     r3h, #11
+        asl     r1, #3
+        asl     r4, #7
+       asl.d   r2, #9
+       asl.d   r2, #21
+
+       asr     r4l, r1h
+       asr     r4, r1h
+        asr.w   r0, r4l
+        asr.w   r4, #7
+        asr.d   r1, #9
+       asr.d   r6, #14
+       asr.d   r5, #27
+
+       bcc     loop
+t7:    bcs     t65
+       beq     t7
+       bg      t65
+       bge     t1
+       bgt     t1
+       bkpt
+       bl      t7
+       ble     loop
+       blt     loop
+       bmi     t2
+       bne     t1
+       bnv     t65
+       bov     t2
+       bpl     t2
+       br      loop
+       call    t2
+       call    [r1]
+
+
diff --git a/as/xa51/local_label.xa b/as/xa51/local_label.xa
new file mode 100644 (file)
index 0000000..7d6f551
--- /dev/null
@@ -0,0 +1,40 @@
+;local lable test... labels consisting of one of more numbers
+;with a dollar sign on the end are considered to be local
+;labels and are unique within a section of code following
+;a normal non-local label
+
+
+        .equ    var, 40
+        .bit    my_bit, var.5  ;this is another comment
+
+begin:
+       add     r5, r1
+       add     r4l, r2h        ;this is the third comment
+       add     r14, r2
+
+00001$:        add     [r5], r11
+       add     [r4], r1h
+
+       jmp     00001$
+       add     r2, [r0]
+       add     r0, r1
+       mov     [r4+4], r6
+       bkpt
+00002$:        mov     r2, r1
+       asr     r1, #3
+       mov     r1l, #9
+       bne     another_function
+       mov     [r1+4], r2
+       jmp     00002$
+       bkpt
+
+
+another_function:
+       add     r4l, [r5]
+       add.b   [r6+], #-10
+00001$: add.b  [r7+32321], #12
+       add.b   1231, #31
+       beq     00001$
+       djnz    r2, 00001$
+       jmp     begin
+
diff --git a/as/xa51/mt.xa b/as/xa51/mt.xa
new file mode 100644 (file)
index 0000000..dfa5ae9
--- /dev/null
@@ -0,0 +1,1425 @@
+
+;$include xa-g3.equ    ;will implement included later on
+
+
+; Include file for XA-G3 SFR Definitions
+; Philips Semiconductors. Revision 1.8, 1/21/97
+; $nolist              ;will implement this later on
+
+; Register equates.
+
+acc     reg     R4L          ; for translated 80C51 code
+dpl     reg     R6L          ; for translated 80C51 code
+dph     reg     R6H          ; for translated 80C51 code
+sp      reg     R7           ; for translated 80C51 code
+
+
+; SFR byte and bit definitions.
+
+bcr     sfr     46ah
+btrh    sfr     469h
+btrl    sfr     468h
+cs      sfr     443h
+ds      sfr     441h
+es      sfr     442h
+
+ieh     sfr     427h
+eri0    bit     ieh.0
+eti0    bit     ieh.1
+eri1    bit     ieh.2
+eti1    bit     ieh.3
+
+iel     sfr     426h
+ex0     bit     iel.0
+et0     bit     iel.1
+ex1     bit     iel.2
+et1     bit     iel.3
+et2     bit     iel.4
+ea      bit     iel.7
+
+ipa0    sfr     4a0h
+ipa1    sfr     4a1h
+ipa2    sfr     4a2h
+ipa4    sfr     4a4h
+ipa5    sfr     4a5h
+
+p0      sfr     430h
+p1      sfr     431h
+p2      sfr     432h
+p3      sfr     433h
+p0cfga  sfr     470h
+p1cfga  sfr     471h
+p2cfga  sfr     472h
+p3cfga  sfr     473h
+p0cfgb  sfr     4f0h
+p1cfgb  sfr     4f1h
+p2cfgb  sfr     4f2h
+p3cfgb  sfr     4f3h
+
+pcon    sfr     404h
+idl     bit     pcon.0
+pd      bit     pcon.1
+
+pswh    sfr     401h
+im0     bit     pswh.0
+im1     bit     pswh.1
+im2     bit     pswh.2
+im3     bit     pswh.3
+rs0     bit     pswh.4
+rs1     bit     pswh.5
+tm      bit     pswh.6
+sm      bit     pswh.7
+
+pswl    sfr     400h
+z       bit     pswl.0
+n       bit     pswl.1
+v       bit     pswl.2
+ac      bit     pswl.6
+cy      bit     pswl.7
+
+psw51   sfr     402h
+
+rth0    sfr     455h
+rth1    sfr     457h
+rtl0    sfr     454h
+rtl1    sfr     456h
+
+s0con   sfr     420h
+ri_0    bit     s0con.0
+ti_0    bit     s0con.1
+rb8_0   bit     s0con.2
+tb8_0   bit     s0con.3
+ren_0   bit     s0con.4
+sm2_0   bit     s0con.5
+sm1_0   bit     s0con.6
+sm0_0   bit     s0con.7
+scon    sfr     420h         ; duplicate label for translated 80C51 code.
+ri      bit     scon.0       ; duplicate label for translated 80C51 code.
+ti      bit     scon.1       ; duplicate label for translated 80C51 code.
+rb8     bit     s0con.2      ; duplicate label for translated 80C51 code.
+tb8     bit     s0con.3      ; duplicate label for translated 80C51 code.
+ren     bit     s0con.4      ; duplicate label for translated 80C51 code.
+sm2     bit     s0con.5      ; duplicate label for translated 80C51 code.
+sm1     bit     s0con.6      ; duplicate label for translated 80C51 code.
+sm0     bit     s0con.7      ; duplicate label for translated 80C51 code.
+
+s0stat  sfr     421h
+stint0  bit     s0stat.0
+oe0     bit     s0stat.1
+br0     bit     s0stat.2
+fe0     bit     s0stat.3
+
+s0buf   sfr     460h
+sbuf    sfr     460h         ; duplicate label for translated 80C51 code.
+s0addr  sfr     461h
+s0aden  sfr     462h
+
+s1con   sfr     424h
+ri_1    bit     s1con.0
+ti_1    bit     s1con.1
+rb8_1   bit     s1con.2
+tb8_1   bit     s1con.3
+ren_1   bit     s1con.4
+sm2_1   bit     s1con.5
+sm1_1   bit     s1con.6
+sm0_1   bit     s1con.7
+
+s1stat  sfr     425h
+stint1  bit     s1stat.0
+oe1     bit     s1stat.1
+br1     bit     s1stat.2
+fe1     bit     s1stat.3
+
+s1buf   sfr     464h
+s1addr  sfr     465h
+s1aden  sfr     466h
+
+scr     sfr     440h
+
+ssel    sfr     403h
+r0seg   bit     ssel.0
+r1seg   bit     ssel.1
+r2seg   bit     ssel.2
+r3seg   bit     ssel.3
+r4seg   bit     ssel.4
+r5seg   bit     ssel.5
+r6seg   bit     ssel.6
+eswen   bit     ssel.7
+
+swe     sfr     47ah
+swr     sfr     42ah
+swr1    bit     swr.0
+swr2    bit     swr.1
+swr3    bit     swr.2
+swr4    bit     swr.3
+swr5    bit     swr.4
+swr6    bit     swr.5
+swr7    bit     swr.6
+
+swe1    equ     00000001q    ; bit masks for software interrupt enables.
+swe2    equ     00000010q
+swe3    equ     00000100q
+swe4    equ     00001000q
+swe5    equ     00010000q
+swe6    equ     00100000q
+swe7    equ     01000000q
+
+t2con   sfr     418h
+cprl2   bit     t2con.0
+ct2     bit     t2con.1
+tr2     bit     t2con.2
+exen2   bit     t2con.3
+tclk0   bit     t2con.4
+rclk0   bit     t2con.5
+tclk    bit     t2con.4      ; duplicate label for translated 80C51 code.
+rclk    bit     t2con.5      ; duplicate label for translated 80C51 code.
+exf2    bit     t2con.6
+tf2     bit     t2con.7
+
+t2mod   sfr     419h
+dcen    bit     t2mod.0
+t2oe    bit     t2mod.1
+t2rd    bit     t2mod.2
+tclk1   bit     t2mod.4
+rclk1   bit     t2mod.5
+
+th2     sfr     459h
+tl2     sfr     458h
+t2caph  sfr     45bh
+t2capl  sfr     45ah
+
+tcon    sfr     410h
+it0     bit     tcon.0
+ie0     bit     tcon.1
+it1     bit     tcon.2
+ie1     bit     tcon.3
+tr0     bit     tcon.4
+tf0     bit     tcon.5
+tr1     bit     tcon.6
+tf1     bit     tcon.7
+
+th0     sfr     451h
+th1     sfr     453h
+tl0     sfr     450h
+tl1     sfr     452h
+tmod    sfr     45ch
+tstat   sfr     411h
+t0oe    bit     tstat.0
+t0rd    bit     tstat.1
+t1oe    bit     tstat.2
+t1rd    bit     tstat.3
+
+wdcon   sfr     41fh
+wdtof   bit     wdcon.1
+wdrun   bit     wdcon.2
+pre0    bit     wdcon.5
+pre1    bit     wdcon.6
+pre2    bit     wdcon.7
+
+wdl     sfr     45fh
+wfeed1  sfr     45dh
+wfeed2  sfr     45eh
+
+
+; Port pin name definitions.
+
+a4d0    bit     P0.0
+a5d1    bit     P0.1
+a6d2    bit     P0.2
+a7d3    bit     P0.3
+a8d4    bit     P0.4
+a9d5    bit     P0.5
+a10d6   bit     P0.6
+a11d7   bit     P0.7
+
+a0      bit     P1.0
+wrh     bit     P1.0
+a1      bit     P1.1
+a2      bit     P1.2
+a3      bit     P1.3
+rxd1    bit     P1.4
+txd1    bit     P1.5
+t2      bit     P1.6
+t2ex    bit     P1.7
+
+a12d8   bit     P2.0
+a13d9   bit     P2.1
+a14d10  bit     P2.2
+a15d11  bit     P2.3
+a16d12  bit     P2.4
+a17d13  bit     P2.5
+a18d14  bit     P2.6
+a19d15  bit     P2.7
+
+rxd0    bit     P3.0
+txd0    bit     P3.1
+int0    bit     P3.2
+int1    bit     P3.3
+t0      bit     P3.4
+t1      bit     P3.5
+busw    bit     P3.5
+wrl     bit     P3.6
+rd      bit     P3.7
+
+; End of XA-G3 SFR definitions.
+;$list         ;handle this later on
+
+
+
+
+trap_delay             equ             0
+trap_cout              equ             1
+
+       org             $8040           ;paulmon2 will jump here on trap #0
+       jmp             syscall_sleep
+       org             $8044           ;paulmon2 will jump here on trap #1
+       jmp             syscall_cout
+
+       org             $8084           ;paulmon2 will jump here on timer0 interrupt
+       jmp             timer0
+
+       org             $80A0           ;paulmon2 will jump here on uart0 rx interrupt
+       jmp             uart0_recv
+       org             $80A4           ;paulmon2 will jump here on uart0 tx interrupt
+       jmp             uart0_xmit
+       org             $80A8           ;paulmon2 will jump here on uart1 rx interrupt
+       jmp             uart1_recv
+       org             $80AC           ;paulmon2 will jump here on uart1 tx interrupt
+       jmp             uart1_xmit
+
+       org             $8100           ;paulmon2 will jump here on software int1
+       jmp             context_switch
+       org             $8104           ;paulmon2 will jump here on software int2
+       jmp             do_events
+
+       org             $8120
+       jmp             poweron
+
+request_do_events                      bit             swr2
+request_context_switch         bit             swr1
+
+max_num_ticks  equ             7               ;max time a process can run if other
+                                                               ;processes are waiting in the run queue
+
+
+;this memory allocation should be done with "ds" directives to
+;save space, but for now it's done with "equ" with lots of gaps,
+;so that things will stay put and aligned on 16 byte boundries
+;where they can be easily viewed with the paulmon2 hex dump.  When
+;the kernel code is more mature this will have to be changed.
+
+sys_stack              equ             $FB00
+
+rx0_buf_head   equ             $FB00           ;2 bytes
+rx0_buf_tail   equ             $FB02           ;2 bytes
+rx0_buf_size   equ             $20
+rx0_buf                        equ             $FB10           ;rx0_buf_size bytes
+
+rx1_buf_head   equ             $FB40           ;2 bytes
+rx1_buf_tail   equ             $FB42           ;2 bytes
+rx1_buf_size   equ             $20
+rx1_buf                        equ             $FB50           ;rx1_buf_size bytes
+
+tx0_buf_head   equ             $FB80           ;2 bytes
+tx0_buf_tail   equ             $FB82           ;2 bytes
+tx0_buf_size   equ             $20
+tx0_buf_threshold      equ     9
+tx0_buf                        equ             $FB90           ;tx0_buf_size bytes
+
+tx1_buf_head   equ             $FBC0           ;2 bytes
+tx1_buf_tail   equ             $FBC2           ;2 bytes
+tx1_buf_size   equ             $20
+tx1_buf_threshold      equ     9
+tx1_buf                        equ             $FBD0           ;tx1_buf_size bytes
+
+
+current_proc   equ             $FEF0   ;2 bytes, ptr to proc_table entry of
+                                                               ;currently running process, 0 if none
+run_queue              equ             $FEF2   ;2 bytes, pointer to a linked list
+                                                               ;of processes waiting to run
+sleep_queue            equ             $FEF4   ;2 bytes, pointer to a linked list
+                                                               ;of sleeping processes
+time_running   equ             $FEF6   ;1 byte, number of ticks that the
+                                                               ;current process has run.
+event_queue            equ             $FCF0   ;2 bytes, pointer to a linked list
+                                                               ;of pending events (event_table)
+
+max_num_proc   equ             14
+proc_table             equ             $FE00   ;(max_num_proc*pt_entry_size) bytes
+pt_entry_size  equ             16              ;16 bytes per table entry
+pt_usp                 equ             0               ;2 bytes, User Stack Pointer
+pt_q_next              equ             2               ;2 bytes, ptr to next in queue
+pt_q_prev              equ             4               ;2 bytes, ptr to prev in queue (+pt_q_next)
+pt_wait                        equ             6               ;2 bytes, what are we waiting for?
+pt_wakeup              equ             8               ;2 bytes, where do we go on wakeup?
+pt_prog_addr   equ             10              ;2 bytes, location of program header
+pt_prog_cs             equ             12              ;1 byte, code segment of program header
+pt_priority            equ             13              ;1 byte, execution priority
+pt_pid                 equ             14              ;1 byte, id num for this process
+
+       ;need to define a program header, which will include various
+       ;info about the program, including the req'd stack space, max
+       ;expect memory to malloc, range of directly addressed memory
+       ;(which must be saved during a context switch), max number of
+       ;open file descriptors reqd, etc.
+
+max_num_event  equ             15              ;(max_num_proc*2)
+event_table            equ             $FC00   ;(max_num_event*evt_entry_size)
+evt_entry_size equ             16              ;8 bytes per event entry
+evt_ticks              equ             0               ;# of ticks until event (delta from prev)
+evt_next               equ             2               ;pointer to next event in list
+evt_function   equ             4               ;place to call (0 if evt slot unused)
+evt_arg1               equ             6               ;optional argument
+
+;the file descriptors will be allocated before the stack in the
+;block of memory which is allocated when the process is created.
+;each individual process can specify the max number of file
+;descriptors that it will require
+
+file_desc_table        equ             $FE80   ;128 bytes
+fd_entry_size  equ             7               ;7 bytes per table entry
+fd_offset              equ             0               ;4 bytes, offset in file
+fd_flags               equ             4               ;1 byte, read/write/append
+fd_routines            equ             5               ;2 bytes, ptr to i/o routines
+
+
+;single character debug symbols:
+;  space       context switcher is idle
+;    *      contect switcher entered
+;    #         timer interrupt (normal)
+;    %         timer interrupt requested context switch
+;    !      timer interrupt requested event service
+;       @              entered event service interrupt routine
+;    ^         event service function called
+;       ~      leaving event service interrupt routine
+
+;this timer interrupt routine is responsible for monitoring the
+;time that the current process has been running and adjusting the
+;process priorities accordingly.  It also checks if any events
+;in the event queue need to be processed.  This code runs at a
+;high cpu priority and it runs often, so it should get done as
+;quickly as possible... if anything lengthy might need to be done,
+;a software interrupt (low priority) should be set, so it can be
+;done later on without blocking other important interrupts, such
+;as low-level i/o.
+
+timer0:
+       push    r0, r1, r2, r3, r4, r5, r6
+       ;other stuff can be added here, such as maintaining a
+       ;system clock...
+         ;mov  r4l, #'#'
+check_events:
+       mov             r0, #event_queue
+       clr             ea
+       mov             r1, [r0]
+       beq             ckevt_end               ;no events if queue is empty
+       mov             r2, [r1+evt_ticks]
+       beq             ckevt_do_em
+       ;if we get here, there are events pending but it's not
+       ;time to do any of them yet, so we'll just decrement the
+       ;ticks of the first one, which will decrement the effective
+       ;time for all of them since they use a delta-time between
+       ;each entry in the linked list
+       adds.w  [r1+evt_ticks], #-1
+       br              ckevt_end
+ckevt_do_em:
+       ;if we get here, there is at least one event which needs
+       ;to be serviced... but we won't do that here, just set the
+       ;software interrupt so it can be handled by "do_event"
+       ;setb   request_do_events
+         ;mov  r4l, #'!'
+         ;call cout
+       ;call   do_events               ;not nice, but sw int priority not working
+       setb    request_do_events
+ckevt_end:
+       setb    ea
+       nop
+schedule:
+       mov             r0, #current_proc
+       clr             ea
+       mov             r1, [r0]
+       beq             sch_end                 ;don't bother if no process is running
+       sub.b   [r1+pt_priority], #1    ;decrease priority by one
+       bcc     sch_priority_ok
+       movs.b  [r1+pt_priority], #0    ;but don't go less than zero
+sch_priority_ok:
+       mov             r0, #time_running
+       mov             r3l, [r0]
+       adds    r3l, #1                         ;increment time_running
+       mov             [r0], r3l
+       cmp             r3l, #max_num_ticks
+       bl              sch_end
+       ;if we get here, the currently running process is has been
+       ;using the cpu for at least the max number ticks, so it's
+       ;time to force it to take a rest and let someone else have
+       ;a chance.
+       mov             r0, #run_queue
+       mov             r1, [r0]
+       beq             sch_end         ;don't preempt if nothing in run queue
+         ;mov  r4l, #'%'
+       setb    request_context_switch          ;make something else run
+sch_end:
+       setb    ea
+         ;cmp  r4l, #'!'
+         ;beq  t0_skip_print
+         ;call cout
+       nop
+t0_skip_print:
+       pop             r0, r1, r2, r3, r4, r5, r6
+       reti
+
+
+       ;this routine actually calls the event handlers for all
+       ;events which are supposed to happen NOW.  The main timer
+       ;routine must decrement the time of the events and arrange
+       ;for this code to be run when any events are supposed to
+       ;happen.  The event handler gets the address of the event
+       ;in r1.
+do_events:
+       push    r0, r1, r2, r3, r4, r5, r6
+do_events_begin:
+         ;mov  r4l, #'@'
+         ;call cout
+doevt_loop:
+       mov             r0, #event_queue
+       mov             r1, [r0]
+       beq             doevt_end               ;no events to do if queue is empty
+       mov             r2, [r1+evt_ticks]
+       bne             doevt_end               ;done if event is in the future
+       mov             r2, [r1+evt_next]
+       mov             [r0], r2                ;remove event from queue
+       mov             r2, [r1+evt_function]
+       movs.w  [r1+evt_function], #0
+         ;mov  r4l, #'^'
+         ;call cout
+       call    [r2]                    ;call the event handler specified
+       br              doevt_loop
+doevt_end:
+         ;mov  r4l, #'~'
+         ;call cout
+       clr             request_do_events
+       pop             r0, r1, r2, r3, r4, r5, r6
+       reti
+
+
+
+;interrupt receive routine for uart #0
+uart0_recv:
+       push    r0, r1, r2, r5, r6
+       clr             ri_0
+       mov             r5l, s0buf
+       mov             r2, #rx0_buf
+       mov             r0, #rx0_buf_head
+       mov             r6, #rx0_buf_tail
+       mov             r1, [r0]
+       adds    r1, #1
+       cjne    r1, #rx0_buf+rx0_buf_size, uart_recv_check2
+       mov             r1, #rx0_buf
+       br              uart_recv_check2
+;interrupt receive routine for uart #1
+uart1_recv:
+       push    r0, r1, r2, r5, r6
+       clr             ri_1
+       mov             r5l, s1buf
+       mov             r2, #rx1_buf
+       mov             r0, #rx1_buf_head
+       mov             r6, #rx1_buf_tail
+       mov             r1, [r0]
+       adds    r1, #1
+       cjne    r1, #rx1_buf+rx1_buf_size, uart_recv_check2
+       mov             r1, #rx1_buf
+;this remaining code is shared by both uart receive routines
+uart_recv_check2:
+       cmp             r1, [r6]
+       bne             uart_recv_ok
+       ;if we get here, the buffer is full, discard data
+       call    wakeup_intr_io
+       pop             r0, r1, r2, r5, r6
+       reti
+uart_recv_ok:
+       mov.b   [r1], r5l               ;store byte into buffer
+       mov             [r0], r1                ;update buffer pointer
+       ;perhaps there should be a way for us to know how much data a
+       ;sleeping process wants to extract from the buffer, so we can
+       ;avoid waking it up until there is at least that much data.
+       call    wakeup_intr_io
+       pop             r0, r1, r2, r5, r6
+       reti
+
+;interrupt transmit routine for uart #0
+uart0_xmit:
+       push    r0, r1, r2, r5, r6
+       clr             ti_0
+       mov             r0, #tx0_buf_tail
+       mov             r2, #tx0_buf_head
+       mov             r1, [r0]
+       cmp             r1, [r2]
+       beq             uart_no_xmit
+       adds    r1, #1
+       cjne    r1, #tx0_buf+tx0_buf_size, uart0_xmit2
+       mov             r1, #tx0_buf
+uart0_xmit2:
+       mov.b   s0buf, [r1]
+       mov             [r0], r1
+       ;now figure out if we want to wake up processes which are
+       ;waiting to put more data into this buffer
+       mov             r1, [r2]
+       sub             r1, [r0]
+       bcc             uart0_xmit3
+       add             r1, #tx0_buf_size
+uart0_xmit3:
+       cmp             r1, #(tx0_buf_threshold)
+       bcc             uart0_xmit_end          ;don't wake proc unless over threshold
+       mov             r2, #tx0_buf
+       call    wakeup_intr_io
+uart0_xmit_end:
+       pop             r0, r1, r2, r5, r6
+       reti
+;interrupt transmit routine for uart #1
+uart1_xmit:
+       push    r0, r1, r2, r5, r6
+       clr             ti_1
+       mov             r0, #tx1_buf_tail
+       mov             r2, #tx1_buf_head
+       mov             r1, [r0]
+       cmp             r1, [r2]
+       beq             uart_no_xmit
+       adds    r1, #1
+       cjne    r1, #tx1_buf+tx1_buf_size, uart1_xmit2
+       mov             r1, #tx1_buf
+uart1_xmit2:
+       mov.b   s1buf, [r1]
+       mov             [r0], r1
+       ;now figure out if we want to wake up processes which are
+       ;waiting to put more data into this buffer
+       mov             r1, [r2]
+       sub             r1, [r0]
+       bcc             uart1_xmit3
+       add             r1, #tx1_buf_size
+uart1_xmit3:
+       cmp             r1, #tx1_buf_threshold
+       bcc             uart1_xmit_end          ;don't wake proc unless over threshold
+       mov             r2, #tx1_buf
+       call    wakeup_intr_io
+uart1_xmit_end:
+       pop             r0, r1, r2, r5, r6
+       reti
+
+;this is shared by both transmit interrupt routines
+uart_no_xmit:
+       ;if we got here, there is no data waiting to transmit
+       ;load the _head pointer with 0, so a routine which loads
+       ;data into the buffer will know to set the ti bit.
+       movs.w  [r2], #0
+       pop             r0, r1, r2, r5, r6
+       reti
+
+
+;This routine wakes up any sleeping processes that are waiting
+;for i/o in the buffer pointed to by r2.  Interrupt routines
+;should call here after doing i/o one their buffers, so that
+;any processes which are waiting for that i/o will be awakened.
+
+wakeup_intr_io:
+       mov             r0, #sleep_queue
+       mov             r1, [r0]
+       beq             wkintio_empty   ;sleep_queue is empty
+       clr             ea                      ;disable interrupts while we look at the queue
+wkintio_loop:
+       cmp             [r1+pt_wait], r2
+       beq             wkintio_wakeup  ;wake proc if waiting for i/o on this buffer
+       mov             r1, [r1+pt_q_next]
+       bne             wkintio_loop
+       setb    ea                              ;ok for other interrupts now
+wkintio_empty:
+       ret
+wkintio_wakeup:
+       ;if we get here, this sleeping processes was waiting for
+       ;i/o on the buffer pointed to by r2, so let's wake it up.
+       ;d_que_proc: (remove from sleep queue)
+       mov             r6, [r1+pt_q_next]
+       push    r6                                              ;save ptr to next in sleep queue
+       mov             r5, [r1+pt_q_prev]
+       mov             [r5], r6
+       mov             [r6+pt_q_prev], r5
+       ;en_que_proc: (add to run queue)
+       mov             r0, #run_queue
+       mov             [r1+pt_q_prev], r0
+       mov             r5, [r0]
+       mov             [r1+pt_q_next], r5
+       lea             r6, r1+pt_q_next
+       mov             [r5+pt_q_prev], r6
+       mov             [r0], r1
+       adds.b  [r1+pt_priority], #5    ;give it a bit of priority boost...
+       ;need to check here for overrange on user priority
+       setb    request_context_switch  ;get it running asap.
+       ;continue looking for processes waiting on this i/o buffer,
+       ;because there may be more than one, and we need to get them
+       ;all into the run queue so that the context switcher can choose
+       ;the one with the highest priority.
+       pop             r1
+       cmp             r1, #0
+       bne             wkintio_loop
+       setb    ea
+       ret
+
+
+
+
+
+
+
+;**************************************************************
+
+
+
+
+
+
+;The context switcher is responsible for figuring out which process
+;is supposed to run next (looks at pt_priority) and if a context
+;change is required this is where it will happen.  If any
+;interrupt driven part of the kernel wants to make a particular
+;process run, it must change the priority for that process and
+;then set the software interrupt to run the context switcher later.
+
+;For system calls that want to make their calling process sleep,
+;they should jump to the ksleep routine, which will jump to
+;"ctsw_begin" to allow something else to run.
+
+;Note: the context switcher doesn't enforce preemptive multitasking...
+;a timer routine must count how long the currently running process
+;has be running since a context switch ("time_running" variable)
+;and if it's being a hog the timer routine that detects it must
+;lower the priority and set the software interrupt to cause this
+;context switcher to make a different process run.  The context
+;switcher also depends on interrupt routines to change the
+;"pt_priority" field of each process table entry, all the context
+;switcher does is pick the process with highest priority.  The
+;currently running process is always switched out if there is
+;something in the run queue, even if the item in the run queue
+;has a lower priority.
+
+
+context_switch:
+       pushu   r0, r1, r2, r3, r4, r5, r6              ;save registers
+       pop             r2, r3, r4                                              ;get pc and psw
+       pushu   r2, r3, r4                                              ;save them on user stack
+       pushu.b ds
+       pushu.b es
+       pushu.b ssel
+
+
+ctsw_begin:
+         ;mov  r4l, #'*'
+         ;call cout
+ctsw_retry:
+       ;find the process on the run queue with highest priority
+       mov             r0, #run_queue
+       mov             r1, [r0]
+       beq             ctsw_queue_empty        ;run_queue is empty
+       movs    r2, #0                  ;r2 is proc w/ highest priority
+       mov             r3l, #0                 ;r3l is highest priority we've seen
+       clr             ea                      ;disable interrupts while we look at the
+                                               ;run queue, since interrupt routines will be
+                                               ;changing priorities to influence which process
+                                               ;should be run next
+ctsw_loop:
+       cmp             [r1+pt_priority], r3l
+       bcs             ctsw_skip
+       ;if we get here, this process is the highest
+       ;priority so far, so forget about any other
+       mov             r3l, [r1+pt_priority]
+       mov             r2, r1
+ctsw_skip:
+       mov             r1, [r1+pt_q_next]
+       bne             ctsw_loop
+       ;now r2 points to the process which should be running...
+       ;check to see if "current_proc" is valid... if it is zero then
+       ;nothing was running (a sys call probably put the process to
+       ;sleep).  If "current_proc" has a valid process pointer, then we
+       ;need to save that process's context (apart from the pushed regs)
+       ;and put that process back into the run queue
+       mov             r0, #current_proc
+       mov             r1, [r0]
+       beq             ctsw_switch
+       call    save_context
+       movs.w  [r1+pt_wakeup], #0              ;wants to go back to user mode
+       mov             r0, #run_queue
+       call    en_que_proc                             ;put back into run queue
+ctsw_switch:
+       ;now it's time to switch to the new process that is about
+       ;to start running.
+       mov             r1, r2
+       call    d_que_proc                      ;remove new proc from run queue
+       call    restore_context
+       mov             r0, #time_running
+       movs.b  [r0], #0                        ;reset "time_running"
+       mov             r0, #current_proc
+       mov             [r0], r1                        ;set "current_proc" to new process
+       ;we may need to return to executing the process in user mode,
+       ;or we may need to jump to a location in the kernel (to continue
+       ;a system call).  If pt_wakup for this process is not zero, then
+       ;some kernel routine caused this process to sleep or be preempted
+       ;and wants to do more work before finally returning to the user
+       ;mode and running the program again.  Usually this is due to a
+       ;system call which needed to put the process to sleep while
+       ;waiting for I/O.
+       mov             r6, [r1+pt_wakeup]
+       setb    ea
+       beq             ctsw_return
+       movs.w  [r1+pt_wakeup], #0      ;don't do this again unless set again
+       jmp             [r6]
+ctsw_return:
+       clr             request_context_switch
+       popu.b  ssel
+       popu.b  es
+       popu.b  ds
+       popu    r2, r3, r4
+    push       r2, r3, r4
+       popu    r0, r1, r2, r3, r4, r5, r6
+       reti
+
+ctsw_queue_empty:
+       ;if we get here, there was no process waiting in the run
+       ;queue.  However, the currently running process isn't
+       ;normally in the run queue, so check if there was a process
+       ;running... if so reset its time slice and let it continue
+       mov             r0, #current_proc
+       mov             r1, [r0]
+       beq             ctsw_idle               ;idle if nothing was running
+       mov             r0, #time_running
+       movs.b  [r0], #0
+       jmp             ctsw_return
+       ;if we get here, the run queue is empty and there in no
+       ;process running now, so we just keep looping inside the
+       ;context switcher until something appears in the run queue
+ctsw_idle:
+       ;need to make a special user-mode idle process... someday
+       ;that could put the processor into idle-mode to save power
+         ;mov  r4l, #' '
+         ;call cout
+       jmp             ctsw_retry
+
+
+
+
+;This "ksleep" routine causes the current process to sleep, afterwhich
+;it jumps to the context switcher, which tries to pick something else to
+;run, if anything is ready.  This code should only be called from
+;within the service code of system calls, which often times need to
+;make their calling process sleep.  R3 should have a value to stuff
+;into "pt_wait" for the process.  ksleep should be CALLed, because
+;it will arrange for the scheduler to return back to the kernel code
+;which called ksleep.  The call to ksleep should be followed by a
+;single NOP, for jump alignment.  Note, before calling ksleep some mechanism
+;to wake the sleeping process back up again must be in place... there
+;is nothing within ksleep which arranges for the process to become
+;awake again, though in the case of i/o the value in r3 should cause it
+;to wake up of the i/o interrupt routine checks for that value.
+
+ksleep:
+       clr             ea
+       mov             r0, #current_proc
+       mov             r1, [r0]                                ;get pointer to process
+         ;cmp  r1, #$FE10
+         ;beq  ksleep_die
+       movs.w  [r0], #0                                ;no current process anymore
+       nop
+ksleep_any:
+       clr             ea
+       mov             [r1+pt_wait], r3                ;record what we're waiting for
+       pop             r5, r6                                  ;get return address
+       adds    r6, #1                                  ;make sure return addr is word addr
+       and             r6, #$FFFE
+       mov             [r1+pt_wakeup], r6              ;set return addr when it wakes up
+       adds.b  [r1+pt_priority], #3    ;increase priority a bit
+       ;need to check here for overrange on user priority
+       mov             r0, #sleep_queue
+       call    en_que_proc                     ;add it to the sleep queue
+       setb    ea
+       call    save_context
+         ;cmp  r1, #$FE10
+         ;beq  ksleep_die
+       jmp             ctsw_begin                              ;scheduler will pick a new
+                                                                       ;process to run (or run idle loop)
+
+ksleep_die:
+       jmp             die
+
+
+syscall_sleep:
+       ;all system calls should run at the same priority as the
+       ;context switcher (level #1), but the code in paulmon2
+       ;sets the trap priority to #8... need to change that.
+       mov.b           pswh, #10000001q
+       pushu   r0, r1, r2, r3, r4, r5, r6              ;save registers
+       pop             r4, r5, r6                                              ;get pc and psw
+       pushu   r4, r5, r6                                              ;save them on user stack
+       pushu.b ds
+       pushu.b es
+       pushu.b ssel
+       ;r0 is number of ticks to sleep, from caller
+       mov             r4, #wake_process
+       mov             r2, #current_proc
+       mov             r5, [r2]        ;"wake_process" will need to know which proc
+       call    add_event
+       mov             r3, #0          ;no other routines will need to know it's sleeping
+       call    ksleep
+       nop
+       popu.b  ssel
+       popu.b  es
+       popu.b  ds
+       popu    r2, r3, r4
+    push       r2, r3, r4
+       popu    r0, r1, r2, r3, r4, r5, r6
+       reti
+
+       ;do_events is supposed to call here when the process is supposed
+       ;to wake up again... all we have to do it remove the process
+       ;from the sleep queue, add it to the run queue, and set the
+       ;software interrupt for the context switcher so it will select
+       ;a new process to run, and it will likely be this one since we
+       ;gave the priority a boost when it was put to sleep.
+       nop
+wake_process:
+         ;mov  r4l, #'$'
+         ;call cout
+       ;r1 should point to event table entry created earlier
+       clr             ea
+       mov             r1, [r1+evt_arg1]       ;now r1 points to process entry
+       call    d_que_proc                      ;remove it from the sleep queue
+       mov             r0, #run_queue
+       call    en_que_proc                     ;add it to the run queue
+       setb    ea
+       ;should really test the priority of this waking process and
+       ;the priority of the currently running one, and only do the
+       ;context switch if the waking one has a higher priority
+       setb    request_context_switch
+       ret
+
+
+
+
+       ;transmit the character in r4l
+syscall_cout:
+       mov.b           pswh, #10000001q
+       pushu   r0, r1, r2, r3, r4, r5, r6              ;save registers
+       pop             r3, r5, r6                                              ;get pc and psw
+       pushu   r3, r5, r6                                              ;save them on user stack
+       pushu.b ds
+       pushu.b es
+       pushu.b ssel
+sc_cout_begin:
+       mov             r0, #tx0_buf_head
+       mov             r2, #tx0_buf_tail
+       clr             ea
+       mov             r1, [r0]
+       bne             sc_cout2
+       ;if we get here, the transmit buffer is empty and the interrupt
+       ;routine cleared the ti bit so no more interrupts are expected
+       setb    ti_0                    ;give the transmitter a kick-start
+       mov             r1, #tx0_buf
+       mov             [r0], r1                ;restore buffer pointers to useful values
+       mov             [r2], r1
+sc_cout2:
+       adds    r1, #1
+       cjne    r1, #tx0_buf+tx0_buf_size, sc_cout3
+       mov             r1, #tx0_buf
+sc_cout3:
+       cmp             r1, [r2]
+       bne             sc_cout4
+       ;if we get here, the buffer is full, so let's just wait
+       ;in a loop... eventually this routine will be replaced
+       ;with "write" which will put the process to sleep and set
+       ;the pt_wait so the context switcher will return here when
+       ;there is space available.  How do we save the variables
+       ;associated with the system call??  Push them onto the
+       ;user's stack for this process?
+       setb    ea
+       mov             r3, #tx0_buf
+       pushu   r4
+       call    ksleep
+       nop
+       popu    r4
+       br              sc_cout_begin   ;try again now that the intr i/o woke us up
+sc_cout4:
+       mov.b   [r1], r4l               ;put data into buffer
+       mov             [r0], r1                ;update pointer
+       setb    ea
+       popu.b  ssel
+       popu.b  es
+       popu.b  ds
+       popu    r2, r3, r4
+    push       r2, r3, r4
+       popu    r0, r1, r2, r3, r4, r5, r6
+       reti
+
+
+
+
+
+
+
+
+       ;this function adds an event to the event queue.  These
+       ;parameters are required:
+       ;       r0      number of ticks until the event should occur
+       ;       r4      function to call when event happens (not zero!!)
+       ;       r5      optional argument
+       ;the event list uses delta time between each entry, so that
+       ;the timer interrupt will only have to deal with the front
+       ;of the queue... but it makes adding an entry here much more
+       ;difficult, but we only have to do this once whereas the
+       ;timer will have to look at the queue however many times the
+       ;evt_ticks value specifies.
+add_event:
+       ;the first thing to do is find a empty place in the table
+       ;to store this event
+       mov             r1, #event_table
+       mov             r2, #max_num_event
+       clr             ea
+aevt_find_slot:
+       mov             r3, [r1+evt_function]
+       beq             aevt_found_slot
+       add             r1, #evt_entry_size
+       djnz    r2, aevt_find_slot
+       ;if we get here there were no slots available, which is a
+       ;very bad thing...
+       jmp             kernel_panic
+aevt_found_slot:
+       ;now that we've located the slot we'll use, let's dump the
+       ;data into it before making a mistake and reusing one of the
+       ;registers that are holding the input parameters
+       mov             [r1+evt_ticks], r0              ;store time value
+       mov             [r1+evt_function], r4   ;store function to be called
+       mov             [r1+evt_arg1], r5               ;store argument
+       ;now we've got to add this event into the linked list in
+       ;the correct time sequence position, and also subtract the
+       ;sum of all the previous evt_ticks values from this one.
+       mov             r6, #event_queue
+       mov             r2, [r6]
+       bne             aevt_check_first
+       ;if we get here, it's easy because the list is empty...
+       mov             [r6], r1
+       movs.w  [r1+evt_next], #0
+       setb    ea
+       ret
+aevt_check_first:
+       ;it may be the case that our new event is supposed to happen
+       ;before the events that are already on the queue
+       cmp             r0, [r2+evt_ticks]
+       bcc             aevt_search
+       mov             [r6], r1
+       mov             [r1+evt_next], r2
+aevt_adjust:
+       ;now that we've added an entry (at r1), we need to adjust the
+       ;evt_ticks values stored in the entry that comes after it.
+       mov             r3, [r1+evt_next]               ;r3 points to next event after ours
+       mov             r4, [r1+evt_ticks]
+       sub             [r3+evt_ticks], r4
+       setb    ea
+       ret
+aevt_search:
+       ;r4 is the adjusted value of ticks for our event as we progress
+       ;through the linked list
+       mov             r4, [r1+evt_ticks]
+aevt_search_loop:
+    sub                r4, [r2+evt_ticks]
+       mov             r3, [r2+evt_next]
+       beq             aevt_add
+       cmp             r4, [r3+evt_ticks]
+       bcs             aevt_add
+       mov             r2, r3
+       jmp             aevt_search_loop
+aevt_add:
+       ;r2 points to the event before the place where we will add
+       ;and r3 points to the place after it in the list
+       mov             [r2+evt_next], r1
+       mov             [r1+evt_next], r3
+       mov             [r1+evt_ticks], r4
+       bne             aevt_adjust                             ;adjust rest of list if it exists
+       setb    ea
+       ret
+
+
+
+
+save_context:          ;process pointer in r1
+       mov             r0, usp
+       mov             [r1+pt_usp], r0                 ;save user stack pointer
+       ;save any directly addressed memory range
+       ;assigned to this process
+       ret
+
+restore_context:               ;process pointer in r1
+       mov             r0, [r1+pt_usp]
+       mov             usp, r0                         ;restore usp to value for new process
+       ;copy contents of directly addresses memory
+       ;back if necessary
+       ret
+
+
+
+
+;This routine removes a process (at r1) from a process queue
+;(run/sleep).  This might be better as an assembler macro, but
+;it's in a subroutine because of the difficulty of figuring out
+;how to correctly manipulate the pointers of a doubly-linked list
+d_que_proc:
+       mov             r6, [r1+pt_q_next]              ;<-- should be macro (begin)
+       mov             r5, [r1+pt_q_prev]
+       mov             [r5], r6
+       mov             [r6+pt_q_prev], r5              ;<--- should be macro (end)
+       ret
+
+;this routine takes a process (at r1) and adds it to a queue, where
+;the location of the head of the queue is in r0.  This is more or
+;less the opposite of "d_que_proc" except that the new process is
+;always added to the head of the list.
+en_que_proc:
+       mov             [r1+pt_q_prev], r0              ;<-- should be macro (begin)
+       mov             r5, [r0]
+       mov             [r1+pt_q_next], r5
+       lea             r6, r1+pt_q_next
+       mov             [r5+pt_q_prev], r6
+       mov             [r0], r1                                ;<--- should be macro (end)
+       ret
+
+;pt_usp                        equ             0               ;2 bytes, User Stack Pointer
+;pt_q_next             equ             2               ;2 bytes, ptr to next in queue
+;pt_q_prev             equ             4               ;2 bytes, ptr to prev in queue (+pt_q_next)
+
+
+;this routine creates a new process, and returns a pointer to it
+;in r1.  If the process can't be created, r1 is zero.  Data about
+;the new process is required:
+;      r4 = location of executable program
+;      r5 = location for user stack (eventually malloc will do this)
+
+
+create_process:
+       mov             r1, #proc_table
+       mov             r2, #max_num_proc
+cproc_find_slot:
+       mov             r0, [r1+pt_usp]
+       beq             cproc_found_slot                ;ok to use this process entry
+       add             r1, #pt_entry_size
+       djnz    r2, cproc_find_slot
+       ;if we get here, there is no more room in the process
+       ;table so we can't create a new process.
+       movs    r1, #0
+       ret
+cproc_found_slot:
+       push    r4, r5
+       ;first initialize the various kernel data required
+       mov             r0, #run_queue
+       clr             ea
+       call    en_que_proc                                     ;add it to the run queue
+       mov.b   [r1+pt_priority], #100          ;start at priority 100
+       movs.w  [r1+pt_wait], #0
+       movs.w  [r1+pt_wakeup], #0
+       ;next we need to set up the process's context
+       pop             r4, r5
+       sub             r5, #26                         ;advance r5 to context storage area
+       mov             [r1+pt_usp], r5
+       movs.w  [r5+], #0                       ;segment select is 0
+       movs.w  [r5+], #0                       ;es is 0
+       movs.w  [r5+], #0                       ;ds is 0
+       movs.w  [r5+], #0                       ;psw is zero (user mode)
+       movs.w  [r5+], #0                       ;run is page zero only (for now)
+       mov             [r5+], r4                       ;set return addr to beginning
+       ;could create initial values for the registers here...
+       setb    ea
+       ret
+
+;user stack while in kernel mode:
+; byte
+; offset   Variable
+;      -2              r6
+;      -4              r5
+;      -6              r4
+;      -8              r3
+;      -10             r2
+;      -12             r1
+;      -14             r0
+;      -16             program counter (lsw)
+;      -18             program counter (msb)
+;      -20             program status word
+;      -22             ds
+;      -24             es
+;      -26             ssel            <-- usp points here
+
+
+poweron:
+       mov             r6, #$F000
+clear_memory_loop:
+       movs.w  [r6+], #0
+       cjne    r6, #0, clear_memory_loop
+         setb  p1.6
+         setb  p1.7
+
+       mov             r7, #sys_stack                          ;set sys stack pointer
+       clr             ea
+       ;initialize serial port buffers
+       mov             r0, #rx0_buf_head
+       mov.w   [r0], #rx0_buf
+       mov             r0, #rx0_buf_tail
+       mov.w   [r0], #rx0_buf
+       mov             r0, #tx0_buf_head
+       movs.w  [r0], #0
+       mov             r0, #tx0_buf_tail
+       movs.w  [r0], #0
+       mov             r0, #rx1_buf_head
+       mov.w   [r0], #rx1_buf
+       mov             r0, #rx1_buf_tail
+       mov.w   [r0], #rx1_buf
+       mov             r0, #tx1_buf_head
+       movs.w  [r0], #0
+       mov             r0, #tx1_buf_tail
+       movs.w  [r0], #0
+       ;clear serial port flags
+       clr             ri_0
+       clr             ti_0
+       clr             ri_1
+       clr             ti_1
+       ;initialize interrupts
+       mov.b   swe, #255                               ;allow all software interrupts
+    mov.b   ipa0, #10010000q           ;priority #9: timer0 interrupt
+       mov.b   ipa4, #10011001q                ;priority #9: uart0 rx and tx interrupts
+       mov.b   ipa5, #10011001q                ;priority #9: uart1 rx and tx interrupts
+       mov.b   ieh, #00001111q                 ;enable uart interrupts
+       ;set up timer 0 to 16-bit auto reload (10 ms)
+       clr             et0                                             ;no timer 0 interrupt yet
+       clr             tr0                                             ;stop timer 0
+       clr             tf0
+       mov.b   rtl0, #low 37798                ;set 10 ms rate
+       mov.b   rth0, #high 37798
+       movs.b  tl0, #0                                 ;zero timer
+       movs.b  th0, #0
+       and.b   tmod, #11110000q                ;set for 16 bit auto reload
+
+
+    ;now set up the variables so we're in idle mode
+       mov             r0, #current_proc
+       movs.w  [r0], #0                                        ;set no process running
+       mov             r0, #run_queue
+       movs.b  [r0], #0                                        ;all queues empty
+       mov             r0, #sleep_queue
+       movs.b  [r0], #0
+       mov             r0, #time_running
+       movs.b  [r0], #0
+       ;initialize pt_usp of all processes to zero, so that
+       ;there won't appear to be any processes
+
+       nop
+clr_proc_tbl:
+       mov             r1, #proc_table
+       mov             r2, #max_num_proc
+clr_proc_tbl_loop:
+       movs.w  [r1+pt_usp], #0
+       add             r1, #pt_entry_size
+       djnz    r2, clr_proc_tbl_loop
+clr_events:
+       mov             r1, #event_table
+       mov             r2, #max_num_event
+clr_events_loop:
+       movs.w  [r1+evt_function], #0
+       add             r1, #evt_entry_size
+       djnz    r2, clr_events_loop
+
+;now create process table entries for the test programs
+       mov             r4, #program1
+       mov             r5, #$F000
+       call    create_process          ;create process for program1
+       mov             r4, #program2
+       mov             r5, #$EC00
+       call    create_process          ;create process for program2
+       mov             r4, #program3
+       mov             r5, #$E800
+       ;call   create_process          ;create process for program3
+
+
+
+       ;now how can we get into user mode for the first time???
+       ;normally the kernel is entered by an interrupt (hardware
+       ;or software) or a trap (sys call), so we get back to a
+       ;user program with reti... but the first time has to be
+       ;handled a bit differently
+       mov             r1, #proc_table
+       call    d_que_proc                      ;remove new proc to run from run queue
+       call    restore_context
+       mov             r0, #time_running
+       movs.b  [r0], #0                        ;reset "time_running"
+       mov             r0, #current_proc
+       mov             [r0], r1                        ;set "current_proc" to new process
+       popu.b  ssel
+       popu.b  es
+       popu.b  ds
+       popu    r0, r1, r2                      ;r0=psw, r1=pc_high, r2=pc_low
+       popu    r3, r4, r5                      ;extract r0/r2 reg from stack
+       popu    r3, r4, r5, r6          ;extract r3/r6 reg from stack
+       mov             cs, r1l                         ;set code segment
+       clr             tf0                                     ;clear timer0 flag
+       setb    tr0                                     ;start timer0
+       setb    et0                                     ;enable timer0 interrupt
+       setb    ea
+       mov             pswl, r0l
+       mov             pswh, r0h                       ;this makes the switch to user mode
+                                                               ;and lower priority to 0 (from max)
+       jmp             [r2]                            ;jump to the user program
+
+
+
+
+kernel_panic:
+die:
+       clr             ea
+       clr             ri_0
+die_loop:
+       jnb             ri_0, die_loop
+       clr             ri_0
+       jmp             $120            ;reboot!
+
+
+
+;unix system calls: (which ones to try first??)
+;process mgt: fork, exec/execve, exit, wait/wait4
+;file i/o: open, read, write, select, ioclt, lseek, close, fcntl
+;signals: kill, signal, alarm, pause
+;pipes: pipe, dup/dup2
+;network/ipc: socket, bind, connect/listen/accept, sendto, recvfrom
+;network/ipc: socketpair, getsockname/getpeername, setsockopt/getsockopt
+;filesystems: chroot/chdir, link, unlink, mkdir, rmdir,
+;filesystems: chown/chmod, stat, rename, truncate
+
+
+
+
+;**************************************************************
+;**                                                          **
+;**                     User Programs                        **
+;**                                                          **
+;**************************************************************
+
+
+
+cout:
+       trap    #trap_cout
+       ret
+
+newline:
+       push    r4l
+       mov             r4l, #13
+       call    cout
+       mov             r4l, #10
+       call    cout
+       pop             r4l
+       ret
+
+pstr:      PUSH    r4
+pstr1:     MOVC    r4l,[r6+]
+           beq     pstr2
+           AND     R4L,#0x7F
+           CALL    cout
+           BR      pstr1
+pstr2:     POP     r4
+           RET
+
+phex:
+phex8:
+           PUSH.B  acc
+           RL.B R4L,#4
+           AND.B   R4L,#15
+           ADD.B   R4L,#246
+           BCC     phex_b
+           ADD.B   R4L,#7
+phex_b:    ADD.B   R4L,#58
+           CALL    cout
+           POP.B   acc
+phex1:     PUSH.B  acc
+           AND.B   R4L,#15
+           ADD.B   R4L,#246
+           BCC     phex_c
+           ADD.B   R4L,#7
+phex_c:    ADD.B   R4L,#58
+           CALL    cout
+           POP.B   acc
+           RET
+
+phex16:
+           PUSH.B  acc
+           MOV.B   R4L,dph
+           CALL    phex
+           MOV.B   R4L,dpl
+           CALL    phex
+           POP.B   acc
+           RET
+
+
+
+
+
+       ;program #1 prints "Paul" to serial port #0, with a
+       ;fairly long delay between each printing
+program1:
+       mov             r6, #str_paul
+       call    pstr
+       mov             r0, #130
+       trap    #trap_delay
+       jmp             program1
+
+str_paul: db "Paul",0
+
+pgm2_speed             equ             10              ;number of ticks between led blinks
+
+       nop
+       ;program #2 blinks the LEDs and prints a "." to the
+       ;serial port #0 after every sequence of blinks.
+program2:
+       or.b    p1cfga, #$C0                    ;p1.6 and p1.7 outputs
+       or.b    p1cfgb, #$C0
+pg2_loop:
+       ;br             pg2_skip_blink
+       setb    p1.6
+       setb    p1.7
+       mov             r0, #pgm2_speed
+       trap    #trap_delay
+       clr             p1.6
+       setb    p1.7
+       mov             r0, #pgm2_speed
+       trap    #trap_delay
+       setb    p1.6
+       setb    p1.7
+       mov             r0, #pgm2_speed
+       trap    #trap_delay
+       setb    p1.6
+       clr             p1.7
+       mov             r0, #pgm2_speed
+       trap    #trap_delay
+       setb    p1.6
+       setb    p1.7
+
+pg2_skip_blink:
+       mov             r4l, #'.'
+       call    cout
+       br              pg2_loop
+
+program3:
+       mov             r4l, #'3'
+       call    cout
+       ;mov            r6, #test_str
+       ;call   pstr
+       jmp             program3
+
+
+test_str: db "This_is_a_test", 0
+
+
+
+
+
+
+
+
+
diff --git a/as/xa51/paulmon2.xa b/as/xa51/paulmon2.xa
new file mode 100644 (file)
index 0000000..6d26aba
--- /dev/null
@@ -0,0 +1,1941 @@
+; this copy is hacked to write to page 0Fxxxx when receiving
+; an intel-hex download... so it hopefully work on Matt's
+; 16-bit board layout.
+
+;$pagewidth 132t
+
+; This is a hacked copy of paulmon2 which is intended to work
+; with the XA processor.  Automatic baud rate detection is not
+; supported, and the extras package is not avaialble.  Flash
+; ROM is not supported.  Paulmon2 only understands the first 64k
+; of memory, and only recognizes intel-hex with 64k address
+; space.  Due to differences in arrangement of the XA, Paulmon2
+; may crash if it receives invalid intel-hex code (untested).
+; See comments in "dnld_ghex" for details about this.
+
+; Other bugs may exist, but the system seems to work.
+
+; Paulmon2 does NOT run in the XA's special 8051 compatibility
+; mode.  Paulmon2 shuts off the watchdog timer and runs all
+; code in system mode (highest priority).
+
+;$include xa-g3.equ
+
+acc     reg     R4L
+dpl     reg     R6L
+dph     reg     R6H
+s0con   sfr     420h
+ri_0    bit     s0con.0
+ti_0    bit     s0con.1
+pswl    sfr     400h
+psw51   sfr     402h
+tcon    sfr     410h
+tr1     bit     tcon.6
+s0buf   sfr     460h
+wdcon   sfr     41fh
+wfeed1  sfr     45dh
+wfeed2  sfr     45eh
+tl1     sfr     452h
+tmod    sfr     45ch
+rtl1    sfr     456h
+ssel   sfr     403h
+cs     sfr     443h
+
+
+; PAULMON2, a user-friendly 8051 monitor, by Paul Stoffregen
+; Please email comments, suggestions, bugs to paul@ece.orst.edu
+
+; It's free.  You may copy sections of code from PAULMON2
+; into your own programs, even for commercial purposes.
+; PAULMON2 should only be distributed free of charge, but may
+; be bundled as 'value-added' with other products, such as
+; development boards, CDROMs, etc.  Please distribute the
+; PAULMON2.DOC file and other files, not just the object code!
+
+; The PAULMON2.EQU and PAULMON2.HDR files contain valuable
+; information that could help you to write programs for use
+; with PAULMON2.
+
+; PAULMON2 is in the public domain. PAULMON2 is distributed in
+; the hope that it will be useful, but without any warranty;
+; without even the implied warranty of merchantability or fitness
+; for a particular purpose.
+
+
+carry  bit     pswl.7
+flag0  bit     psw51.5
+flag1  bit     psw51.1
+
+;---------------------------------------------------------;
+;                                                         ;
+;           PAULMON2's default configuration              ;
+;                                                         ;
+;---------------------------------------------------------;
+
+; PAULMON2 should be assembled using the modified AS31 assembler,
+; originally written by Ken Stauffer, many small changes by Paul
+; Stoffregen.  This free assembler is available on the web at
+; http://www.ece.orst.edu/~paul/8051-goodies/goodies-index.html
+; As well, these web pages have a fill-out form which makes it
+; very easy to custom configure PAULMON2.  Using this form will
+; edit the code for you, run the AS31 assmebler, and send you the
+; object code to program into your chip.
+
+
+vector         equ     $8000
+
+; These three parameters tell PAULMON2 where the user's memory is
+; installed.  "bmem" and "emem" define the space that will be searched
+; for program headers, user installed commands, start-up programs, etc.
+; "bmem" and "emem" should be use so they exclude memory areas where
+; perphreal devices may be mapped, as reading memory from an io chip
+; may reconfigure it unexpectedly.  If flash rom is used, "bmem" and "emem"
+; should also include the space where the flash rom is mapped.
+
+pgm        EQU     $F000            ;default location for the user program
+bmem       EQU     $0000            ;where is the beginning of memory
+emem       EQU     $FFFF            ;end of the memory
+
+
+; Please note... much of the memory management code only looks at the
+; upper 8 bits of an address, so it's not a good idea to somehow map
+; your memory chips (with complex address decoding logic) into chunks
+; less than 256 bytes.  In other words, only using a piece of a flash
+; rom chip and mapping it between C43A to F91B would confuse PAULMON2
+; (as well as require quit a bit of address decoding logic circuitry)
+
+; To set the baud rate, use this formula or set to 0 for auto detection
+; baud_const = 256 - (crystal / (4 * 16 * baud))  <-- for XA chip
+
+;baud_const EQU                247             ;19200 baud w/ 11.0592 MHz (XA)
+;baud_const EQU                253             ;57600 baud w/ 11.0592 MHz (XA)
+baud_const EQU         251             ;57600 baud w/ 18.432 MHz (XA)
+;baud_const EQU                241             ;19200 baud w/ 18.432 MHz (XA)
+;baud_const EQU                236             ;19531 baud w/ 25 MHz (XA)
+
+
+; About download speed: when writing to ram, PAULMON2 can accept data
+; at the maximum baud rate (baud_const=255 or 57600 baud w/ 11.0592 MHz).
+; Most terminal emulation programs introduce intentional delays when
+; sending ascii data, which you would want to turn off for downloading
+; larger programs into ram.  For Flash ROM, the maximum speed is set by
+; the time it takes to program each location... 9600 baud seems to work
+; nicely for the AMD 28F256 chip.  The "character pacing" delay in a
+; terminal emulation program should be sufficient to download to flash
+; rom and any baud rate.
+
+
+; Several people didn't like the key definations in PAULMON1.
+; Actually, I didn't like 'em either, but I never took the time
+; to change it.  Eventually I got used to them, but now it's
+; really easy to change which keys do what in PAULMON2.  You
+; can guess what to do below, but don't use lowercase.
+
+help_key   EQU     '?'              ;help screen
+dir_key    EQU     'M'              ;directory
+run_key    EQU     'R'              ;run program
+dnld_key   EQU     'D'              ;download
+upld_key   EQU     'U'              ;upload
+nloc_key   EQU     'N'              ;new memory location
+jump_key   EQU     'J'              ;jump to memory location
+dump_key   EQU     'H'              ;hex dump memory
+intm_key   EQU     'I'              ;hex dump internal memory
+edit_key   EQU     'E'              ;edit memory
+clrm_key   EQU     'C'              ;clear memory
+erfr_key   EQU     'Z'              ;erase flash rom
+xdump_key  EQU     'X'              ;hex dump ext data memory
+
+
+;location of parameter table used by download command
+;sixteen bytes of internal memory are required beginning at this
+;location
+
+dnld_parm  EQU     $01F0
+stack      EQU     $01EE
+
+;---------------------------------------------------------;
+;                                                         ;
+;                    Interrupt Vectors                    ;
+;  (and little bits of code crammed in the empty spaces)  ;
+;                                                         ;
+;---------------------------------------------------------;
+
+           ORG     0
+
+                       dw      $8f00, poweron
+breakvec:      dw      $8f00, vector + breakvec
+tracevec:      dw      $8f00, vector + tracevec
+stackovvec:    dw      $8f00, vector + stackovvec
+div0vec:       dw      $8f00, vector + div0vec
+uretivec:      dw      $8f00, vector + uretivec
+                       org     $40
+trap0vec:      dw      $8800, vector + trap0vec
+trap1vec:      dw      $8800, vector + trap1vec
+trap2vec:      dw      $8800, vector + trap2vec
+trap3vec:      dw      $8800, vector + trap3vec
+trap4vec:      dw      $8800, vector + trap4vec
+trap5vec:      dw      $8800, vector + trap5vec
+trap6vec:      dw      $8800, vector + trap6vec
+trap7vec:      dw      $8800, vector + trap7vec
+trap8vec:      dw      $8800, vector + trap8vec
+trap9vec:      dw      $8800, vector + trap9vec
+trap10vec:     dw      $8800, vector + trap10vec
+trap11vec:     dw      $8800, vector + trap11vec
+trap12vec:     dw      $8800, vector + trap12vec
+trap13vec:     dw      $8800, vector + trap13vec
+trap14vec:     dw      $8800, vector + trap14vec
+trap15vec:     dw      $8800, vector + trap15vec
+                       org     $80
+int0vec:       dw      $8900, vector + int0vec
+tmr0vec:       dw      $8900, vector + tmr0vec
+int1vec:       dw      $8900, vector + int1vec
+tmr1vec:       dw      $8900, vector + tmr1vec
+tmr2vec:       dw      $8900, vector + tmr2vec
+                       org     $A0
+rxd0vec:       dw      $8900, vector + rxd0vec
+txd0vec:       dw      $8900, vector + txd0vec
+rxd1vec:       dw      $8900, vector + rxd1vec
+txd1vec:       dw      $8900, vector + txd1vec
+                       org     $100
+swi1vec:       dw      $8100, vector + swi1vec
+swi2vec:       dw      $8200, vector + swi2vec
+swi3vec:       dw      $8300, vector + swi3vec
+swi4vec:       dw      $8400, vector + swi4vec
+swi5vec:       dw      $8500, vector + swi5vec
+swi6vec:       dw      $8600, vector + swi6vec
+swi7vec:       dw      $8700, vector + swi7vec
+
+          org     $120
+
+               jmp             poweron
+
+
+space:     MOV.B   R4L,#' '
+           CALL    cout
+           RET
+
+dash:      MOV.B   R4L,#'-'
+           CALL    cout
+           RET
+
+
+;---------------------------------------------------------;
+;                                                         ;
+;       The jump table for user programs to call          ;
+;             subroutines within PAULMON                  ;
+;                                                         ;
+;---------------------------------------------------------;
+
+; no jump table anymore... use TRAP or conditional assembly.
+
+
+;---------------------------------------------------------;
+;                                                         ;
+;              Subroutines for serial I/O                 ;
+;                                                         ;
+;---------------------------------------------------------;
+
+
+;these are the only three routines which access the serial port directly
+;but poweron/autobaud to the initial setup
+
+cin:       JNB     ri_0,cin
+           CLR     ri_0
+           MOV.B   R4L,s0buf
+           RET
+cout:      JNB     ti_0,cout
+           CLR     ti_0               ;clr ti before the mov to sbuf!
+           MOV.B   s0buf,R4L
+           RET
+esc:  ;checks to see if <ESC> is waiting on serial port
+      ;C=clear if no <ESC>, C=set if <ESC> pressed
+      ;buffer is flushed
+           PUSH.B  acc
+           CLR     carry
+           JNB     ri_0,esc2
+          clr     ri_0
+           MOV.B   R4L, s0buf
+           CJNE.B  R4L,#27,esc2
+           SETB    carry
+esc2:      POP.B   acc
+           RET
+
+
+;everything else from here on should only access the serial
+;port using cin/cout/etc
+
+
+newline:   PUSH.B  acc
+           MOV.B   R4L,#13
+           CALL    cout
+           MOV.B   R4L,#10
+           CALL    cout
+           POP.B   acc
+           RET
+
+       ;get 2 digit hex number from serial port
+       ; c = set if ESC pressed, clear otherwise
+       ; flag0 = set if return w/ no input, clear otherwise
+ghex:
+ghex8:     CLR     flag0
+ghex8c:
+           CALL    cin              ;get first digit
+           CALL    upper
+           CJNE.B  R4L,#27,ghex8f
+ghex8d:    SETB    carry
+           MOVS    R4L,#0
+           RET
+ghex8f:    CJNE.B  R4L,#13,ghex8h
+           SETB    flag0
+           CLR     carry
+           MOVS    R4L,#0
+           RET
+ghex8h:    MOV.B   R1L,R4L
+           CALL    asc2hex
+           BCS     ghex8c
+           XCH.B   R4L,R1L          ;r2 will hold hex value of 1st digit
+           CALL    cout
+ghex8j:
+           CALL    cin              ;get second digit
+           CALL    upper
+           CJNE.B  R4L,#27,ghex8k
+           BR      ghex8d
+ghex8k:    CJNE.B  R4L,#13,ghex8m
+           MOV.B   R4L,R1L
+           CLR     carry
+           RET
+ghex8m:    CJNE.B  R4L,#8,ghex8p
+ghex8n:    CALL    cout
+           BR      ghex8c
+ghex8p:    CJNE.B  R4L,#21,ghex8q
+           BR      ghex8n
+ghex8q:    MOV.B   R1H,R4L
+           CALL    asc2hex
+           BCS     ghex8j
+           XCH.B   R4L,R1H
+           CALL    cout
+           MOV.B   R4L,R1L
+           RL.B R4L,#4
+           OR.B    R4L,R1H
+           CLR     carry
+           RET
+
+
+
+
+       ;carry set if esc pressed
+       ;flag0 set if return pressed w/ no input
+ghex16:
+           MOV.B   R1L,#0           ;start out with 0
+           MOV.B   R1H,#0
+           MOV.B   R2L,#4           ;number of digits left
+           CLR     flag0
+
+ghex16c:
+           CALL    cin
+           CALL    upper
+           CJNE.B  R4L,#27,ghex16d
+           SETB    carry            ;handle esc key
+           MOVS    R4L,#0
+           MOV.B   dph,R4L
+           MOV.B   dpl,R4L
+           RET
+ghex16d:   CJNE.B  R4L,#8,ghex16f
+           BR      ghex16k
+ghex16f:   CJNE.B  R4L,#127,ghex16g ;handle backspace
+ghex16k:   CJNE.B  R2L,#4,ghex16e   ;have they entered anything yet?
+           BR      ghex16c
+ghex16e:   CALL    cout
+           CALL    ghex16y
+           ADDS.B  R2L,#1
+           BR      ghex16c
+ghex16g:   CJNE.B  R4L,#13,ghex16i  ;return key
+           MOV.B   dph,R1H
+           MOV.B   dpl,R1L
+           CJNE.B  R2L,#4,ghex16h
+           MOVS    R4L,#0
+           MOV.B   dph,R4L
+           MOV.B   dpl,R4L
+           SETB    flag0
+ghex16h:   CLR     carry
+           RET
+ghex16i:   MOV.B   R2H,R4L          ;keep copy of original keystroke
+           CALL    asc2hex
+           BCS     ghex16c
+           XCH.B   R4L,R2H
+           CALL    cout
+           MOV.B   R4L,R2H
+           PUSH.B  acc
+           CALL    ghex16x
+           POP.B   acc
+           ADD.B   R4L,R1L
+           MOV.B   R1L,R4L
+           MOVS    R4L,#0
+           ADDC.B  R4L,R1H
+           MOV.B   R1H,R4L
+           DJNZ.B  R2L,ghex16c
+           CLR     carry
+           MOV.B   dpl,R1L
+           MOV.B   dph,R1H
+           RET
+
+ghex16x:  ;multiply r3-r2 by 16 (shift left by 4)
+           MOV.B   R4L,R1H
+           RL.B R4L,#4
+           AND.B   R4L,#11110000Q
+           MOV.B   R1H,R4L
+           MOV.B   R4L,R1L
+           RL.B R4L,#4
+           AND.B   R4L,#00001111Q
+           OR.B    R4L,R1H
+           MOV.B   R1H,R4L
+           MOV.B   R4L,R1L
+           RL.B R4L,#4
+           AND.B   R4L,#11110000Q
+           MOV.B   R1L,R4L
+           RET
+
+ghex16y:  ;divide r3-r2 by 16 (shift right by 4)
+           MOV.B   R4L,R1L
+           RL.B R4L,#4
+           AND.B   R4L,#00001111Q
+           MOV.B   R1L,R4L
+           MOV.B   R4L,R1H
+           RL.B R4L,#4
+           AND.B   R4L,#11110000Q
+           OR.B    R4L,R1L
+           MOV.B   R1L,R4L
+           MOV.B   R4L,R1H
+           RL.B R4L,#4
+           AND.B   R4L,#00001111Q
+           MOV.B   R1H,R4L
+           RET
+
+asc2hex:             ;carry set if invalid input
+           CLR     carry
+           PUSH.B  R4H
+           SUBB.B  R4L,#'0'
+           MOV.B   R4H,R4L
+           SUBB.B  R4L,#10
+           BCS     a2h1
+           MOV.B   R4L,R4H
+           SUBB.B  R4L,#7
+           MOV.B   R4H,R4L
+a2h1:      MOV.B   R4L,R4H
+           CLR     carry
+           AND.B   R4L,#11110000Q   ;just in case
+           JZ      a2h2
+           SETB    carry
+a2h2:      MOV.B   R4L,R4H
+           POP.B   R4H
+           RET
+
+
+phex:
+phex8:
+           PUSH.B  acc
+           RL.B R4L,#4
+           AND.B   R4L,#15
+           ADD.B   R4L,#246
+           BCC     phex_b
+           ADD.B   R4L,#7
+phex_b:    ADD.B   R4L,#58
+           CALL    cout
+           POP.B   acc
+phex1:     PUSH.B  acc
+           AND.B   R4L,#15
+           ADD.B   R4L,#246
+           BCC     phex_c
+           ADD.B   R4L,#7
+phex_c:    ADD.B   R4L,#58
+           CALL    cout
+           POP.B   acc
+           RET
+
+
+phex16:
+           PUSH.B  acc
+           MOV.B   R4L,dph
+           CALL    phex
+           MOV.B   R4L,dpl
+           CALL    phex
+           POP.B   acc
+           RET
+
+
+;a not so well documented feature of pstr is that you can print
+;multiple consecutive strings without needing to reload dptr
+;(which takes 3 bytes of code!)... this is useful for inserting
+;numbers or spaces between strings.
+
+pstr:      PUSH.B  acc
+pstr1:     MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           ADDS.W  R6,#1
+           JZ      pstr2
+           MOV     C,R4L.7
+           AND.B   R4L,#0x7F
+           CALL    cout
+           BCS     pstr2
+           BR      pstr1
+pstr2:     POP.B   acc
+           RET
+
+
+
+;---------------------------------------------------------;
+;                                                         ;
+;    The 'high-level' stuff to interact with the user     ;
+;                                                         ;
+;---------------------------------------------------------;
+
+
+menu: ;first we print out the prompt
+           MOV.W   R6,#prompt1      ;give 'em the first part of prompt
+           CALL    pcstr
+           MOV.B   R4L,R3H
+           CALL    phex
+           MOV.B   R4L,R3L
+           CALL    phex
+           CALL    space
+       MOV.W   R6,#prompt2
+           CALL    pstr
+
+
+;now we're past the prompt, so let's get some input
+           CALL    cin              ;get the input
+           CALL    upper
+
+
+menu1a:    CJNE.B  R4L,#help_key,menu1b
+           MOV.W   R6,#help_cmd2
+           CALL    pcstr
+           call     help
+               jmp     menu
+menu1b:    CJNE.B  R4L,#dir_key,menu1c
+           MOV.W   R6,#dir_cmd
+           CALL    pcstr
+           call     dir
+               jmp     menu
+menu1c:    CJNE.B  R4L,#run_key,menu1d
+           MOV.W   R6,#run_cmd
+           CALL    pcstr
+           call        run
+               jmp     menu
+menu1d:    CJNE.B  R4L,#dnld_key,menu1e
+           MOV.W   R6,#dnld_cmd
+           CALL    pcstr
+           call     dnld
+               jmp             menu
+menu1e:    CJNE.B  R4L,#upld_key,menu1f
+           MOV.W   R6,#upld_cmd
+           CALL    pcstr
+           call     upld
+               jmp             menu
+menu1f:    CJNE.B  R4L,#nloc_key,menu1g
+           MOV.W   R6,#nloc_cmd
+           CALL    pcstr
+           call     nloc
+               jmp             menu
+menu1g:    CJNE.B  R4L,#jump_key,menu1h
+           MOV.W   R6,#jump_cmd
+           CALL    pcstr
+           call        jump
+               jmp             menu
+menu1h:    CJNE.B  R4L,#dump_key,menu1i
+           MOV.W   R6,#dump_cmd
+           CALL    pcstr
+           call        dump
+               jmp             menu
+menu1i:    CJNE.B  R4L,#edit_key,menu1j
+           MOV.W   R6,#edit_cmd
+           CALL    pcstr
+           call        edit
+               jmp             menu
+menu1j:    CJNE.B  R4L,#clrm_key,menu1l
+           MOV.W   R6,#clrm_cmd
+           CALL    pcstr
+           call   clrm
+               jmp             menu
+menu1l:    CJNE.B  R4L,#intm_key,menu1m
+           MOV.W   R6,#intm_cmd
+           CALL    pcstr
+           call        intm
+               jmp             menu
+menu1m:    CJNE.B  R4L,#xdump_key,menu1n
+           MOV.W   R6,#xdump_cmd
+           CALL    pcstr
+           call        xdump
+               jmp             menu
+menu1n:
+
+    ;invalid input, no commands to run...
+menu_end:                       ;at this point, we have not found
+           CALL    newline          ;anything to run, so we give up.
+               jmp             menu
+
+;..........................................................
+
+;---------------------------------------------------------;
+
+;dnlds1 = "Begin sending Intel HEX format file <ESC> to abort"
+;dnlds2 = "Download aborted"
+;dnlds3 = "Download completed"
+
+
+;16 byte parameter table: (eight 16 bit values)
+;  *   0 = lines received
+;  *   1 = bytes received
+;  *   2 = bytes written
+;  *   3 = bytes unable to write
+;  *   4 = incorrect checksums
+;  *   5 = unexpected begin of line
+;  *   6 = unexpected hex digits (while waiting for bol)
+;  *   7 = unexpected non-hex digits (in middle of a line)
+
+dnld:
+           MOV.W   R6,#dnlds1
+           CALL    pcstr          ;"begin sending file <ESC> to abort"
+       mov.w   r5,#dnld_parm
+        mov.b   R1L,#8
+dnld0:  movs.w  [r5+],#0          ;initialize all parameters to 0
+        DJNZ.B  R1L,dnld0
+
+         ;look for begining of line marker ':'
+dnld1:     CALL    cin
+           CJNE.B  R4L,#27,dnld2    ;Test for escape
+           BR      dnld_esc
+dnld2:     CJNE.B  R4L,#':',dnld2b
+       mov.w   r5, #dnld_parm
+       adds.w  [r5+0], #1
+           BR      dnld3
+dnld2b:   ;check to see if it's a hex digit, error if it is
+           CALL    asc2hex
+           BCS     dnld1
+        mov.w   r5, #dnld_parm
+        adds.w  [r5+12], #1
+           BR      dnld1
+         ;begin taking in the line of data
+dnld3:     MOV.B   R4L,#'.'
+           CALL    cout
+           MOV.B   R2L,#0           ;r4 will count up checksum
+           CALL    dnld_ghex
+           MOV.B   R0L,R4L          ;R0 = # of data bytes
+           MOV.B   R2L,R4L
+           CALL    dnld_ghex
+           MOV.B   dph,R4L          ;High byte of load address
+           ADD.B   R4L,R2L
+           MOV.B   R2L,R4L
+           CALL    dnld_ghex
+           MOV.B   dpl,R4L          ;Low byte of load address
+           ADD.B   R4L,R2L
+           MOV.B   R2L,R4L
+           CALL    dnld_ghex        ;Record type
+           MOV.B   R1L,R4L
+           ADD.B   R4L,R2L
+           MOV.B   R2L,R4L
+           MOV.B   R4L,R1L
+           CJNE.B  R4L,#1,dnld4     ;End record?
+           BR      dnld_end
+dnld4:
+dnld5:     CALL    dnld_ghex        ;Get data byte
+           MOV.B   R1L,R4L
+        mov.w   r5, #dnld_parm
+        adds.w  [r5+2], #1
+           MOV.B   R4L,R1L
+           ADD.B   R4L,R2L
+           MOV.B   R2L,R4L
+           MOV.B   R4L,R1L
+           CALL    smart_wr         ;c=1 if an error writing
+           MOVS    R4L,#0
+           ADDC.B  R4L,#2
+           MOV.B   R0H,R4L
+;     2 = bytes written
+;     3 = bytes unable to write
+       rl.b    r4l, #1
+       movs.b  r4h, #0
+       mov.w   r5, #dnld_parm
+       add.w   r5, r4
+       adds.w  [r5], #1
+
+           ADDS.W  R6,#1
+           DJNZ.B  R0L,dnld5
+
+           CALL    dnld_ghex        ;get checksum
+           ADD.B   R4L,R2L
+           JZ      dnld1            ;should always add to zero
+dnld_sumerr:
+        mov.w   r5, #dnld_parm
+        adds.w  [r5+8], #1
+           BR      dnld1
+
+dnld_end:   ;handles the proper end-of-download marker
+           CALL    dnld_ghex        ;get the last checksum
+       ;assume no data in this line.
+           ADD.B   R4L,R2L
+           JNZ     dnld_sumerr
+           CALL    dnld_dly
+           MOV.W   R6,#dnlds3
+           CALL    pcstr          ;"download went ok..."
+       ;consume any cr or lf character that may have been
+       ;on the end of the last line
+           JNB     ri_0,dnld_sum
+           CALL    cin
+           BR      dnld_sum
+
+dnld_esc:   ;handle esc received in the download stream
+           CALL    dnld_dly
+           MOV.W   R6,#dnlds2
+           CALL    pcstr          ;"download aborted."
+           BR      dnld_sum
+
+dnld_dly:   ;a short delay since most terminal emulation programs
+           ;won't be ready to receive anything immediately after
+           ;they've transmitted a file... even on a fast Pentium(tm)
+           ;machine with 16550 uarts!
+           MOV.B   R0L,#0
+dnlddly2:  MOV.B   R0H,#0
+dnlddly3:  DJNZ.B  R0H,dnlddly3       ;roughly 128k cycles, appox 0.1 sec
+           DJNZ.B  R0L,dnlddly2
+           RET
+
+
+
+
+
+
+;a special version of ghex just for the download.  Does not
+;look for carriage return or backspace.  Handles ESC key by
+;poping the return address (I know, nasty, but it saves many
+;bytes of code in this 4k ROM) and then jumps to the esc
+;key handling.  This ghex doesn't echo characters, and if it
+;sees ':', it pops the return and jumps to an error handler
+;for ':' in the middle of a line.  Non-hex digits also jump
+;to error handlers, depending on which digit.
+
+; XA compatibility issue: This code pops the return address
+; off the stack and does a jump to an error handler.  The
+; code should examine the mode bit to see if return addresses
+; are 16 or 32 bits on the stack.  As it is, it will probably
+; work in non 16-bit mode (default), but this code could
+; use some attention to this detail nonetheless.
+
+dnld_ghex:
+dnldgh1:   CALL    cin
+           CALL    upper
+           CJNE.B  R4L,#27,dnldgh3
+dnldgh2:   POP.B   acc                 ;<-- compatibility problem!!!  Check XA mode
+           POP.B   acc                 ;<-- and pop correct number of words off stack
+           BR      dnld_esc
+dnldgh3:   CJNE.B  R4L,#':',dnldgh5
+dnldgh4:
+        mov.w   r5, #dnld_parm
+        adds.w  [r5+10], #1    ;handle unexpected beginning of line
+           POP.B   acc                 ;<-- compatibility problem!!!  Check XA mode
+           POP.B   acc                 ;<-- and pop correct number of words off stack
+           JMP     dnld3            ;and now we're on a new line!
+dnldgh5:   CALL    asc2hex
+           BCC     dnldgh6
+        mov.w   r5, #dnld_parm
+        adds.w  [r5+14], #1
+           BR      dnldgh1
+dnldgh6:   MOV.B   R1L,R4L          ;keep first digit in r2
+dnldgh7:   CALL    cin
+           CALL    upper
+           CJNE.B  R4L,#27,dnldgh8
+           BR      dnldgh2
+dnldgh8:   CJNE.B  R4L,#':',dnldgh9
+           BR      dnldgh4
+dnldgh9:   CALL    asc2hex
+           BCC     dnldghA
+        mov.w   r5, #dnld_parm
+        adds.w  [r5+14], #1
+           BR      dnldgh7
+dnldghA:   XCH.B   R4L,R1L
+           RL.B R4L,#4
+           OR.B    R4L,R1L
+           RET
+
+;dnlds4 =  "Summary:"
+;dnlds5 =  " lines received"
+;dnlds6a = " bytes received"
+;dnlds6b = " bytes written"
+
+dnld_sum:    ;print out download summary
+           MOV.W   R6,#dnlds4
+           CALL    pcstr
+        MOV.w   r5, #dnld_parm
+       mov.w   r6, [r5+]
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds5
+           CALL    pcstr
+       mov.w   r6, [r5+]
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds6a
+           CALL    pcstr
+       mov.w   r6, [r5+]
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds6b
+           CALL    pcstr
+dnld_err:    ;now print out error summary
+           MOV.w   r5, #dnld_parm + 6
+                       mov     r1l, #5
+dnlder2:
+               mov.w   r6, [r5+]
+           bne     dnlder3          ;any errors?
+           DJNZ.B  R1L,dnlder2
+        ;no errors, so we print the nice message
+           MOV.W   R6,#dnlds13
+           CALL    pcstr
+           RET
+dnlder3:  ;there were errors, so now we print 'em
+           MOV.W   R6,#dnlds7
+           CALL    pcstr
+         ;but let's not be nasty... only print if necessary
+               mov             r5, #dnld_parm+6
+               mov             r6, [r5+]
+        beq     dnlder4
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds8
+           CALL    pcstr
+dnlder4:
+               mov             r6, [r5+]
+               beq             dnlder5
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds9
+           CALL    pcstr
+dnlder5:
+               mov             r6, [r5+]
+        beq     dnlder6
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds10
+           CALL    pcstr
+dnlder6:
+        mov     r6, [r5+]
+        beq     dnlder7
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds11
+           CALL    pcstr
+dnlder7:
+        mov     r6, [r5+]
+        beq     dnlder8
+           CALL    space
+           CALL    pint16u
+           MOV.W   R6,#dnlds12
+           CALL    pcstr
+dnlder8:   CALL    newline
+           RET
+
+;dnlds7:  = "Errors:"
+;dnlds8:  = " bytes unable to write"
+;dnlds9:  = " incorrect checksums"
+;dnlds10: = " unexpected begin of line"
+;dnlds11: = " unexpected hex digits"
+;dnlds12: = " unexpected non-hex digits"
+;dnlds13: = "No errors detected"
+
+
+
+;---------------------------------------------------------;
+
+
+jump:
+           MOV.W   R6,#prompt8
+           CALL    pcstr
+       mov.w   r6, r3
+           CALL    phex16
+           MOV.W   R6,#prompt4
+           CALL    pcstr
+           CALL    ghex16
+           JB      flag0,jump3
+           BCC     jump2
+           MOV.W   R6,#abort
+           CALL    pcstr
+           CALL    newline
+           RET
+jump2:         mov.w   r3, r6
+jump3:     CALL    newline
+           MOV.W   R6,#runs1
+           CALL    pcstr
+                       mov.w   r6, r3
+                       call    phex16
+                       call    newline
+                       mov.w   r6, r3
+
+jump_doit:  ;jump to user code @dptr (this used by run command also)
+       movs.w  r0, #0
+       push.w  r0
+       mov.w  r0, #$120
+       push.w  r0
+       jmp     [r6]
+
+
+;---------------------------------------------------------;
+
+dump:
+           MOV.B   R1L,#16          ;number of lines to print
+           CALL    newline
+           CALL    newline
+dump1:
+       push    r6
+       mov     r6, #msg_code
+       call    pstr
+       pop     r6
+          MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+           CALL    phex16           ;tell 'em the memory location
+           MOV.B   R4L,#':'
+           CALL    cout
+           CALL    space
+           MOV.B   R1H,#16          ;r3 counts # of bytes to print
+           MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+dump2:     MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           ADDS.W  R6,#1
+           CALL    phex             ;print each byte in hex
+           CALL    space
+           DJNZ.B  R1H,dump2
+           CALL    space            ;print a couple extra space
+           CALL    space
+           MOV.B   R1H,#16
+           MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+dump3:     MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           ADDS.W  R6,#1
+           AND.B   R4L,#01111111Q   ;avoid unprintable characters
+           CLR     carry
+           SUBB.B  R4L,#20h
+           BCC     dump4            ;avoid control characters
+           MOV.B   R4L,#(' ' - 20h)
+dump4:     ADD.B   R4L,#20h
+           CALL    cout
+           DJNZ.B  R1H,dump3
+           CALL    newline
+           MOV.B   R3L,dpl
+           MOV.B   R3H,dph
+           CALL    esc
+           BCS     dump5
+           DJNZ.B  R1L,dump1        ;loop back up to print next line
+dump5:     CALL    newline
+           RET
+
+msg_code: db "Code:",0
+msg_xram: db "Xram:",0
+
+;---------------------------------------------------------;
+
+
+xdump:
+           MOV.B   R1L,#16          ;number of lines to print
+           CALL    newline
+           CALL    newline
+xdump1:        
+       push    r6
+       mov     r6, #msg_xram
+       call    pstr
+       pop     r6
+       MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+           CALL    phex16           ;tell 'em the memory location
+           MOV.B   R4L,#':'
+           CALL    cout
+           CALL    space
+           MOV.B   R1H,#16          ;r3 counts # of bytes to print
+           MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+xdump2:    MOVS    R4L,#0
+           MOVX                  r4l,[r6]
+           ADDS.W  R6,#1
+           CALL    phex             ;print each byte in hex
+           CALL    space
+           DJNZ.B  R1H,xdump2
+           CALL    space            ;print a couple extra space
+           CALL    space
+           MOV.B   R1H,#16
+           MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+xdump3:    MOVS    R4L,#0
+           MOVX                  r4l,[r6]
+           ADDS.W  R6,#1
+           AND.B   R4L,#01111111Q   ;avoid unprintable characters
+           CLR     carry
+           SUBB.B  R4L,#20h
+           BCC     xdump4            ;avoid control characters
+           MOV.B   R4L,#(' ' - 20h)
+xdump4:    ADD.B   R4L,#20h
+           CALL    cout
+           DJNZ.B  R1H,xdump3
+           CALL    newline
+           MOV.B   R3L,dpl
+           MOV.B   R3H,dph
+           CALL    esc
+           BCS     xdump5
+           DJNZ.B  R1L,xdump1        ;loop back up to print next line
+xdump5:    CALL    newline
+           RET
+
+
+
+
+;---------------------------------------------------------;
+
+nloc:
+           MOV.W   R6,#prompt6
+           CALL    pcstr
+           CALL    ghex16
+           BCS     nloc2
+           JB      flag0,nloc2
+           MOV.B   R3L,dpl
+           MOV.B   R3H,dph
+           CALL    newline
+           CALL    newline
+           RET
+nloc2:     MOV.W   R6,#abort
+           CALL    pcstr
+           CALL    newline
+           RET
+
+;---------------------------------------------------------;
+
+
+edit:      ;edit external ram...
+           MOV.W   R6,#edits1
+           CALL    pcstr
+           MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+edit1:     CALL    phex16
+           MOV.B   R4L,#':'
+           CALL    cout
+           CALL    space
+           MOV.B   R4L,#'('
+           CALL    cout
+           MOV.B   R3L,dpl
+           MOV.B   R3H,dph
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CALL    phex
+           MOV.W   R6,#prompt10
+           CALL    pcstr
+           CALL    ghex
+           JB      flag0,edit2
+           BCS     edit2
+           MOV.B   dpl,R3L
+           MOV.B   dph,R3H
+           MOVX.B  [R6],R4L
+           CALL    newline
+           ADDS.W  R6,#1
+           MOV.B   R3L,dpl
+           MOV.B   R3H,dph
+           JMP     edit1
+edit2:     MOV.W   R6,#edits2
+           CALL    pcstr
+           RET
+
+;---------------------------------------------------------;
+
+dir:
+           MOV.W   R6,#prompt9
+           CALL    pcstr
+           MOV.B   R0L,#21
+dir0a:     CALL    space
+           DJNZ.B  R0L,dir0a
+           MOV.W   R6,#prompt9b
+           CALL    pcstr
+
+           MOV.B   dph,#high bmem
+dir1:      CALL    find             ;find the next program in memory
+           BCS     dir2
+dir_end:   CALL    newline          ;we're done if no more found
+           RET
+dir2:
+           CALL    space
+           CALL    space
+           MOV.B   dpl,#32          ;print its name
+           CALL    pstr
+           MOV.B   dpl,#32          ;how long is the name
+           CALL    lenstr
+           MOV.B   R4L,#33
+           CLR     carry
+           SUBB.B  R4L,R0L
+           MOV.B   R0L,R4L
+           MOV.B   R4L,#' '         ;print the right # of spaces
+dir3:      CALL    cout
+           DJNZ.B  R0L,dir3
+           MOV.B   dpl,#0
+           CALL    phex16           ;print the memory location
+           MOV.B   R0L,#6
+           MOV.B   R4L,#' '
+dir4:      CALL    cout
+           DJNZ.B  R0L,dir4
+           MOV.B   dpl,#4           ;now figure out what type it is
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           MOV.B   R1L,dph          ;save this, we're inside a search
+
+dir5:      CJNE.B  R4L,#254,dir5b
+           MOV.W   R6,#type1        ;it's an external command
+           BR      dir7
+dir5b:     CJNE.B  R4L,#255,dir5c
+           MOV.W   R6,#type1a       ;it's some system thing
+           BR      dir7
+dir5c:     CJNE.B  R4L,#253,dir5d
+           MOV.W   R6,#type1b       ;it's a startup routine
+           BR      dir7
+dir5d:     CJNE.B  R4L,#35,dir5e
+           MOV.W   R6,#type2        ;it's an ordinary program
+           BR      dir7
+dir5e:     CJNE.B  R4L,#143,dir5f
+           MOV.W   R6,#type3        ;it's a background task
+           BR      dir7
+dir5f:     CJNE.B  R4L,#69,dir5g
+           MOV.W   R6,#type4        ;it's a data file
+           BR      dir7
+dir5g:
+dir6:      MOV.W   R6,#type5        ;who knows what the hell it is
+
+dir7:      CALL    pcstr          ;print out the type
+           MOV.B   dph,R1L          ;go back and find the next one
+           CALL    newline
+           CJNE.B  dph,#high emem, dir8     ;did we just print the last one?
+           JMP     dir_end
+dir8:      ADDS.B  dph,#1
+           JMP     dir1
+
+
+;   type1=Ext Command
+;  type1a=Misc System Thing
+;  type1b=Power-Up Code
+;   type2=Program
+;   type3=Background Task
+;   type4=Data File
+;   type5=???????
+
+;---------------------------------------------------------;
+
+
+run:
+           CALL    newline
+           CALL    newline
+       jmp     0xF000
+
+
+;---------------------------------------------------------;
+
+help:
+           MOV.W   R6,#help1txt
+           CALL    pcstr
+           MOV.B   R2L,#help_key
+           MOV.W   R6,#help_cmd
+           CALL    help2
+           MOV.B   R2L,#dir_key
+           MOV.W   R6,#dir_cmd
+           CALL    help2
+           MOV.B   R2L,#run_key
+           MOV.W   R6,#run_cmd
+           CALL    help2
+           MOV.B   R2L,#dnld_key
+           MOV.W   R6,#dnld_cmd
+           CALL    help2
+           MOV.B   R2L,#upld_key
+           MOV.W   R6,#upld_cmd
+           CALL    help2
+           MOV.B   R2L,#nloc_key
+           MOV.W   R6,#nloc_cmd
+           CALL    help2
+           MOV.B   R2L,#jump_key
+           MOV.W   R6,#jump_cmd
+           CALL    help2
+           MOV.B   R2L,#dump_key
+           MOV.W   R6,#dump_cmd
+           CALL    help2
+           MOV.B   R2L,#xdump_key
+           MOV.W   R6,#xdump_cmd
+           CALL    help2
+           MOV.B   R2L,#intm_key
+           MOV.W   R6,#intm_cmd
+           CALL    help2
+           MOV.B   R2L,#edit_key
+           MOV.W   R6,#edit_cmd
+           CALL    help2
+           MOV.B   R2L,#clrm_key
+           MOV.W   R6,#clrm_cmd
+           CALL    help2
+           MOV.W   R6,#help2txt
+           CALL    pcstr
+           MOV.W   R6,#bmem
+help3:     CALL    find
+           BCC     help4
+           MOV.B   dpl,#4
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CJNE.B  R4L,#254,help3a  ;only FE is an ext command
+           CALL    space
+           CALL    space
+           ADDS.B  dpl,#1
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CALL    cout
+           CALL    dash
+           CALL    space
+           MOV.B   dpl,#32
+           CALL    pstr
+           CALL    newline
+help3a:    ADDS.B  dph,#1
+           BR      help3
+help4:
+           CALL    newline
+           RET
+
+help2:                          ;print 11 standard lines
+           CALL    space            ;given key in R4 and name in dptr
+           CALL    space
+           MOV.B   R4L,R2L
+           CALL    cout
+           CALL    dash
+           CALL    space
+           CALL    pcstr
+           CALL    newline
+           RET
+
+;---------------------------------------------------------;
+
+upld:
+
+           CALL    get_mem
+       ;assume we've got the beginning address in r3/r2
+       ;and the final address in r5/r4 (r4=lsb)...
+
+       ;print out what we'll be doing
+           MOV.W   R6,#uplds3
+           CALL    pcstr
+           MOV.B   R4L,R1H
+           CALL    phex
+           MOV.B   R4L,R1L
+           CALL    phex
+           MOV.W   R6,#uplds4
+           CALL    pcstr
+           MOV.B   R4L,R2H
+           CALL    phex
+           MOV.B   R4L,R2L
+           CALL    phex
+           CALL    newline
+
+       ;need to adjust end location by 1...
+           MOV.B   dph,R2H
+           MOV.B   dpl,R2L
+           ADDS.W  R6,#1
+           MOV.B   R2L,dpl
+           MOV.B   R2H,dph
+
+           MOV.W   R6,#prompt7
+           CALL    pcstr
+           CALL    cin
+           CJNE.B  R4L,#27,upld2e
+           JMP     abort_it
+upld2e:    CALL    newline
+           MOV.B   dpl,R1L
+           MOV.B   dph,R1H
+
+upld3:     MOV.B   R4L,R2L          ;how many more bytes to output??
+           CLR     carry
+           SUBB.B  R4L,dpl
+           MOV.B   R1L,R4L
+           MOV.B   R4L,R2H
+           SUBB.B  R4L,dph
+           JNZ     upld4            ;if >256 left, then do next 16
+           MOV.B   R4L,R1L
+           JZ      upld7            ;if we're all done
+           AND.B   R4L,#11110000Q
+           JNZ     upld4            ;if >= 16 left, then do next 16
+           BR      upld5            ;otherwise just finish it off
+upld4:     MOV.B   R1L,#16
+upld5:     MOV.B   R4L,#':'         ;begin the line
+           CALL    cout
+           MOV.B   R4L,R1L
+           CALL    phex             ;output # of data bytes
+           CALL    phex16           ;output memory location
+           MOV.B   R4L,dph
+           ADD.B   R4L,dpl
+           ADD.B   R4L,R1L
+           MOV.B   R1H,R4L          ;r3 will become checksum
+           MOVS    R4L,#0
+           CALL    phex             ;output 00 code for data
+upld6:     MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CALL    phex             ;output each byte
+           ADD.B   R4L,R1H
+           MOV.B   R1H,R4L
+           ADDS.W  R6,#1
+           DJNZ.B  R1L,upld6        ;do however many bytes we need
+           MOV.B   R4L,R1H
+           CPL.B   R4L
+           ADDS.B  R4L,#1
+           CALL    phex             ;and finally the checksum
+           CALL    newline
+           CALL    esc
+           BCC     upld3            ;keep working if no esc pressed
+           BR      abort_it
+upld7:     MOV.B   R4L,#':'
+           CALL    cout
+           MOVS    R4L,#0
+           CALL    phex
+           CALL    phex
+           CALL    phex
+           ADDS.B  R4L,#1
+           CALL    phex
+           MOV.B   R4L,#255
+           CALL    phex
+upld8:     CALL    newline
+           CALL    newline
+           RET
+
+
+;---------------------------------------------------------;
+
+get_mem:     ;this thing gets the begin and end locations for
+            ;a few commands.  If an esc or enter w/ no input,
+            ;it pops it's own return and returns to the menu
+            ;(nasty programming, but we need tight code for 4k rom)
+           CALL    newline
+           CALL    newline
+           MOV.W   R6,#beg_str
+           CALL    pcstr
+           CALL    ghex16
+           BCS     pop_it
+           JB      flag0,pop_it
+           PUSH.B  dph
+           PUSH.B  dpl
+           CALL    newline
+           MOV.W   R6,#end_str
+           CALL    pcstr
+           CALL    ghex16
+           MOV.B   R2H,dph
+           MOV.B   R2L,dpl
+           POP.B   acc
+           MOV.B   R1L,R4L
+           POP.B   acc
+           MOV.B   R1H,R4L
+           BCS     pop_it
+           JB      flag0,pop_it
+           CALL    newline
+           RET
+
+pop_it:    POP.B   acc
+           POP.B   acc
+abort_it:
+           CALL    newline
+           MOV.W   R6,#abort
+           CALL    pcstr
+           CALL    newline
+           RET
+
+clrm:
+           CALL    get_mem
+           MOV.W   R6,#sure
+           CALL    pcstr
+           CALL    cin
+           CALL    upper
+           CJNE.B  R4L,#'Y',abort_it
+           CALL    newline
+           CALL    newline
+     ;now we actually do it
+
+clrm2:     MOV.B   dph,R1H
+           MOV.B   dpl,R1L
+clrm3:     MOVS    R4L,#0
+           CALL    smart_wr
+               cmp.w   r6, r2
+               bne             clrm4
+           RET
+clrm4:     ADDS.W  R6,#1
+           BR      clrm3
+
+
+
+;---------------------------------------------------------;
+
+intm:      CALL    newline
+           MOVs.w   r5,#0
+intm2:     CALL    newline
+           CJNE.w  r5, #512, intm3
+           CALL    newline
+           RET
+intm3:  mov.b  r4l,r5h
+       call    phex   
+       MOV.B   R4L,R5L
+           CALL    phex
+           MOV.B   R4L,#':'
+           CALL    cout
+intm4:     CALL    space
+           MOV.B   R4L,[r5+]
+           CALL    phex
+           MOV.B   R4L,R5L
+           AND.B   R4L,#00001111Q
+           JNZ     intm4
+           BR      intm2
+
+
+
+;---------------------------------------------------------;
+;                                                         ;
+;   Subroutines for memory managment and non-serial I/O   ;
+;                                                         ;
+;---------------------------------------------------------;
+
+
+;finds the next header in the external memory.
+;  Input DPTR=point to start search (only MSB used)
+;  Output DPTR=location of next module
+;    C=set if a header found, C=clear if no more headers
+find:      MOV.B   dpl,#0
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CJNE.B  R4L,#0xA5,find3
+           ADDS.W  R6,#1
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CJNE.B  R4L,#0xE5,find3
+           ADDS.W  R6,#1
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CJNE.B  R4L,#0xE0,find3
+           ADDS.W  R6,#1
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CJNE.B  R4L,#0xA5,find3
+           MOV.B   dpl,#0           ;found one here!
+           SETB    carry
+           RET
+find3:
+           CJNE.B  dph, #high emem, find4    ;did we just check the end
+           CLR     carry
+           RET
+find4:     ADDS.B  dph,#1           ;keep on searching
+           BR      find
+
+
+
+
+;Write to ordinary RAM.  Carry bit will indicate
+;if the value was successfully written, C=1 if not written.
+
+
+smart_wr:
+           PUSH.B  acc
+           PUSH.B  R4H
+           MOV.B   R4H,R4L
+wr_ram:    MOV.B   R4L,R4H
+           MOVX.B  [R6],R4L         ;write the value to memory
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]       ;read it back from code memory
+           CLR     carry
+           SUBB.B  R4L,R4H
+           JZ      smwrok
+           MOVX.B  R4L,[R6]         ;read it back from data memory
+           CLR     carry
+           SUBB.B  R4L,R4H
+           JZ      smwrok
+smwrbad:   SETB    carry
+           BR      smwrxit
+smwrok:    CLR     carry
+smwrxit:   POP.B   R4H
+           POP.B   acc
+           RET
+
+
+
+;---------------------------------------------------------;
+;                                                         ;
+;       Power-On initialization code and such...          ;
+;                                                         ;
+;---------------------------------------------------------;
+
+;first the hardware has to get initialized.
+
+poweron:
+       mov.w   r7, #stack
+       mov.b   wdcon, #0       ;shut off watchdog
+       mov.b   wfeed1, #$a5
+       mov.b   wfeed2, #$5a
+
+       ;or.b   p1cfga, #$C0            ;for testing
+       ;or.b   p1cfgb, #$C0            ;for testing
+
+       ;clr            p1.7            ;for testing
+
+       ;no automatic baud rate detection anymore
+autobaud:
+           MOV.B   tcon,#0
+           MOV.B   R4L,#baud_const
+           MOV.B   tl1,R4L
+           MOV.B   rtl1,R4L
+           MOV.B   tmod,#$22       ;set timer #1 for 8 bit auto-reload
+           MOV.B   s0con,#$52
+           SETB    tr1              ;start the baud rate timer
+           MOV.w   r5, #2000
+ab_loop1:  DJNZ.w  r5, ab_loop1
+
+       ;clr            p1.6            ;for testing
+
+
+welcome:
+           MOV.B   R0L,#24
+welcm2:    CALL    newline
+           DJNZ.B  R0L,welcm2
+
+       ;setb   p1.7            ;for testing
+
+           MOV.B   R0L,#15
+           MOV.B   R4L,#' '
+welcm4:    CALL    cout
+           DJNZ.B  R0L,welcm4
+           MOV.W   R6,#logon1
+           CALL    pcstr
+           MOV.W   R6,#logon2
+           CALL    pcstr
+           ;CALL    dir              ;skip dir for now
+               mov.w   r3, #pgm
+           JMP     menu
+
+
+
+
+
+
+;---------------------------------------------------------;
+;                                                         ;
+;     More subroutines, but less frequent used            ;
+;                                                         ;
+;---------------------------------------------------------;
+
+
+
+
+
+pint8u: ;prints the unsigned 8 bit value in Acc in base 10
+           PUSH.B  R4H
+           PUSH.B  acc
+           BR      pint8b
+
+pint8:  ;prints the signed 8 bit value in Acc in base 10
+           PUSH.B  R4H
+           PUSH.B  acc
+           JNB     R4L.7,pint8b
+           MOV.B   R4L,#'-'
+           CALL    cout
+           POP.B   acc
+           PUSH.B  acc
+           CPL.B   R4L
+           ADD.B   R4L,#1
+pint8b:    MOV.B   R4H,#100
+           DIVU.B R4L,R4H
+           SETB    flag0
+           JZ      pint8c
+           CLR     flag0
+           ADD.B   R4L,#'0'
+           CALL    cout
+pint8c:    MOV.B   R4L,R4H
+           MOV.B   R4H,#10
+           DIVU.B R4L,R4H
+           JNB     flag0,pint8d
+           JZ      pint8e
+pint8d:    ADD.B   R4L,#'0'
+           CALL    cout
+pint8e:    MOV.B   R4L,R4H
+           ADD.B   R4L,#'0'
+           CALL    cout
+           POP.B   acc
+           POP.B   R4H
+           RET
+
+
+
+       ;print 16 bit unsigned integer in DPTR (r6), using base 10.
+pint16u:  
+                       push.w  r2, r3, r4
+           CLR     flag0
+pint16a:   ;ten-thousands digit
+                       mov.w   r2, r6
+                       movs.w  r3, #0
+                       div.d   r2, #10000
+                       xch             r2, r3
+                       mov.b   r4l, r3l
+            JZ      pint16b
+           ADD.B   R4L,#'0'
+           CALL    cout
+           SETB    flag0
+pint16b:   ;thousands digit
+                       movs.w  r3, #0
+                       div.d   r2, #1000
+                       xch             r2, r3
+                       mov.b   r4l, r3l
+           JNZ     pint16c
+           JNB     flag0,pint16d
+pint16c:   ADD.B   R4L,#'0'
+           CALL    cout
+           SETB    flag0
+pint16d:   ;hundreds digit
+                       div.w   r2, #100
+                       xch             r2h, r2l
+                       mov.b   r4l, r2h
+           JNZ     pint16e
+           JNB     flag0,pint16f
+pint16e:   ADD.B   R4L,#'0'
+           CALL    cout
+           SETB    flag0
+pint16f:    ;tens digit
+           DIVU.B R2L, #10
+                       mov.b   r4l, r2l
+           JNZ     pint16g
+           JNB     flag0,pint16h
+pint16g:   ADD.B   R4L,#'0'
+           CALL    cout
+pint16h:  ;ones digit
+                  MOV.B   R4L,R2h          ;and finally the ones digit
+           ADD.B   R4L,#'0'
+           CALL    cout
+           pop.w       r2, r3, r4
+           RET
+
+
+
+upper:  ;converts the ascii code in Acc to uppercase, if it is lowercase
+           PUSH.B  acc
+           CLR     carry
+           SUBB.B  R4L,#97
+           BCS     upper2           ;is it a lowercase character
+           SUBB.B  R4L,#26
+           BCC     upper2
+           POP.B   acc
+           ADD.B   R4L,#224         ;convert to uppercase
+           RET
+upper2:    POP.B   acc              ;don't change anything
+           RET
+
+
+lenstr:    MOV.B   R0L,#0           ;returns length of a string in r0
+           PUSH.B  acc
+lenstr1:   MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           JZ      lenstr2
+           MOV     C,R4L.7
+           ADDS.B  R0L,#1
+           BCS     lenstr2
+           ADDS.W  R6,#1
+           BR      lenstr1
+lenstr2:   POP.B   acc
+           RET
+
+
+;pcstr prints the compressed strings.  A dictionary of 128 words is
+;stored in 4 bit packed binary format.  When pcstr finds a byte in
+;a string with the high bit set, it prints the word from the dictionary.
+;A few bytes have special functions and everything else prints as if
+;it were an ordinary string.
+
+; special codes for pcstr:
+;    0 = end of string
+;   13 = CR/LF
+;   14 = CR/LF and end of string
+;   31 = next word code should be capitalized
+
+pcstr:         push    r0, r2, r4
+           SETB    flag1
+           SETB    flag0
+pcstr1:    MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           ADDS.W  R6,#1
+           JZ      pcstr2
+           JB      R4L.7,decomp
+           AND.B   R4L,#0x7F
+pcstrs1:   CJNE.B  R4L,#13,pcstrs2
+           CALL    newline
+           SETB    flag1
+           BR      pcstr1
+pcstrs2:   CJNE.B  R4L,#31,pcstrs3
+           CLR     flag0
+           BR      pcstr1
+pcstrs3:   CJNE.B  R4L,#14,pcstrs4
+           CALL    newline
+           BR      pcstr2
+pcstrs4:
+           CLR     flag1
+           CALL    cout
+           BR      pcstr1
+pcstr2:    pop         r0, r2, r4
+           RET
+
+;dcomp actually takes care of printing a word from the dictionary
+
+; dptr = position in packed words table
+; r4=0 if next nibble is low, r4=255 if next nibble is high
+
+decomp:    AND.B   R4L,#0x7F
+           MOV.B   R0L,R4L          ;r0 counts which word
+           JB      flag1,decomp1    ;avoid leading space if first word
+           CALL    space
+decomp1:   CLR     flag1
+                       push    r6
+           MOV.W   R6,#words
+           MOV.B   R2L,#0
+           MOV.B   R4L,R0L
+           JZ      dcomp3
+       ;here we must seek past all the words in the table
+       ;that come before the one we're supposed to print
+           MOV.B   R0H,R4L
+dcomp2:    CALL    get_next_nibble
+           JNZ     dcomp2
+       ;when we get here, a word has been skipped... keep doing
+       ;this until we're pointing to the correct one
+           DJNZ.B  R0H,dcomp2
+dcomp3:        ;now we're pointing to the correct word, so all we have
+       ;to do is print it out
+           CALL    get_next_nibble
+           JZ      dcomp_end
+           CJNE.B  R4L,#15,dcomp4
+       ;the character is one of the 12 least commonly used
+           CALL    get_next_nibble
+           ADDS.B  R4L,#1
+           MOVC.B  A,[A+pc]
+           BR      dcomp5
+           DB      "hfwgybxvkqjz"
+dcomp4:        ;the character is one of the 14 most commonly used
+           ADDS.B  R4L,#1
+           MOVC.B  A,[A+pc]
+           BR      dcomp5
+           DB      "etarnisolumpdc"
+dcomp5:        ;decide if it should be uppercase or lowercase
+           MOV     C,flag0
+           MOV     R4L.5,C
+           SETB    flag0
+           CJNE.B  R0L,#20,dcomp6
+           CLR     R4L.5
+dcomp6:    CJNE.B  R0L,#12,dcomp7
+           CLR     R4L.5
+dcomp7:    CALL    cout
+           BR      dcomp3
+dcomp_end:
+                       pop             r6
+           JMP     pcstr1
+
+get_next_nibble:       ;...and update dptr and r4, of course
+           MOVS    R4L,#0
+           MOVC.B  A,[A+dptr]
+           CJNE.B  R2L,#0,gnn2
+           MOV.B   R2L,#255
+           AND.B   R4L,#00001111Q
+           RET
+gnn2:      MOV.B   R2L,#0
+           ADDS.W  R6,#1
+           RL.B R4L,#4
+           AND.B   R4L,#00001111Q
+           RET
+
+
+;---------------------------------------------------------;
+;                                                         ;
+;        Here begins the data tables and strings          ;
+;                                                         ;
+;---------------------------------------------------------;
+
+;this is the dictionary of 128 words used by pcstr.
+
+words:
+           DB      0x82,0x90,0xE8,0x23,0x86,0x05,0x4C,0xF8
+           DB      0x44,0xB3,0xB0,0xB1,0x48,0x5F,0xF0,0x11
+           DB      0x7F,0xA0,0x15,0x7F,0x1C,0x2E,0xD1,0x40
+           DB      0x5A,0x50,0xF1,0x03,0xBF,0xBA,0x0C,0x2F
+           DB      0x96,0x01,0x8D,0x3F,0x95,0x38,0x0D,0x6F
+           DB      0x5F,0x12,0x07,0x71,0x0E,0x56,0x2F,0x48
+           DB      0x3B,0x62,0x58,0x20,0x1F,0x76,0x70,0x32
+           DB      0x24,0x40,0xB8,0x40,0xE1,0x61,0x8F,0x01
+           DB      0x34,0x0B,0xCA,0x89,0xD3,0xC0,0xA3,0xB9
+           DB      0x58,0x80,0x04,0xF8,0x02,0x85,0x60,0x25
+           DB      0x91,0xF0,0x92,0x73,0x1F,0x10,0x7F,0x12
+           DB      0x54,0x93,0x10,0x44,0x48,0x07,0xD1,0x26
+           DB      0x56,0x4F,0xD0,0xF6,0x64,0x72,0xE0,0xB8
+           DB      0x3B,0xD5,0xF0,0x16,0x4F,0x56,0x30,0x6F
+           DB      0x48,0x02,0x5F,0xA8,0x20,0x1F,0x01,0x76
+           DB      0x30,0xD5,0x60,0x25,0x41,0xA4,0x2C,0x60
+           DB      0x05,0x6F,0x01,0x3F,0x26,0x1F,0x30,0x07
+           DB      0x8E,0x1D,0xF0,0x63,0x99,0xF0,0x42,0xB8
+           DB      0x20,0x1F,0x23,0x30,0x02,0x7A,0xD1,0x60
+           DB      0x2F,0xF0,0xF6,0x05,0x8F,0x93,0x1A,0x50
+           DB      0x28,0xF0,0x82,0x04,0x6F,0xA3,0x0D,0x3F
+           DB      0x1F,0x51,0x40,0x23,0x01,0x3E,0x05,0x43
+           DB      0x01,0x7A,0x01,0x17,0x64,0x93,0x30,0x2A
+           DB      0x08,0x8C,0x24,0x30,0x99,0xB0,0xF3,0x19
+           DB      0x60,0x25,0x41,0x35,0x09,0x8E,0xCB,0x19
+           DB      0x12,0x30,0x05,0x1F,0x31,0x1D,0x04,0x14
+           DB      0x4F,0x76,0x12,0x04,0xAB,0x27,0x90,0x56
+           DB      0x01,0x2F,0xA8,0xD5,0xF0,0xAA,0x26,0x20
+           DB      0x5F,0x1C,0xF0,0xF3,0x61,0xFE,0x01,0x41
+           DB      0x73,0x01,0x27,0xC1,0xC0,0x84,0x8F,0xD6
+           DB      0x01,0x87,0x70,0x56,0x4F,0x19,0x70,0x1F
+           DB      0xA8,0xD9,0x90,0x76,0x02,0x17,0x43,0xFE
+           DB      0x01,0xC1,0x84,0x0B,0x15,0x7F,0x02,0x8B
+           DB      0x14,0x30,0x8F,0x63,0x39,0x6F,0x19,0xF0
+           DB      0x11,0xC9,0x10,0x6D,0x02,0x3F,0x91,0x09
+           DB      0x7A,0x41,0xD0,0xBA,0x0C,0x1D,0x39,0x5F
+           DB      0x07,0xF2,0x11,0x17,0x20,0x41,0x6B,0x35
+           DB      0x09,0xF7,0x75,0x12,0x0B,0xA7,0xCC,0x48
+           DB      0x02,0x3F,0x64,0x12,0xA0,0x0C,0x27,0xE3
+           DB      0x9F,0xC0,0x14,0x77,0x70,0x11,0x40,0x71
+           DB      0x21,0xC0,0x68,0x25,0x41,0xF0,0x62,0x7F
+           DB      0xD1,0xD0,0x21,0xE1,0x62,0x58,0xB0,0xF3
+           DB      0x05,0x1F,0x73,0x30,0x77,0xB1,0x6F,0x19
+           DB      0xE0,0x19,0x43,0xE0,0x58,0x2F,0xF6,0xA4
+           DB      0x14,0xD0,0x23,0x03,0xFE,0x31,0xF5,0x14
+           DB      0x30,0x99,0xF8,0x03,0x3F,0x64,0x22,0x51
+           DB      0x60,0x25,0x41,0x2F,0xE3,0x01,0x56,0x27
+           DB      0x93,0x09,0xFE,0x11,0xFE,0x79,0xBA,0x60
+           DB      0x75,0x42,0xEA,0x62,0x58,0xA0,0xE5,0x1F
+           DB      0x53,0x4F,0xD1,0xC0,0xA3,0x09,0x42,0x53
+           DB      0xF7,0x12,0x04,0x62,0x1B,0x30,0xF5,0x05
+           DB      0xF7,0x69,0x0C,0x35,0x1B,0x70,0x82,0x2F
+           DB      0x2F,0x14,0x4F,0x51,0xC0,0x64,0x25,0x00
+
+;STR
+
+logon1:    DB      "Welcome",128,148,"2 (beta7,XA) by",31,248,31,254,13,14
+logon2:    DB      32,32,"See",148,"2.DOC,",148,"2.EQU",164
+           DB      148,"2.HDR",180,213,141,".",14
+abort:     DB      " ",31,158,31,160,"!",14
+prompt1:   DB      148,"2 (beta7,XA) Loc:",0
+prompt2:   DB      ">",160
+prompt3:   DB      134,202,130,'(',0
+prompt4:   DB      "),",149,140,128,200,": ",0
+prompt5:   DB      31,151,130,195,"s",199,166,131,","
+           DB      186," JUMP",128,134,161,"r",130,13,14
+prompt6:   DB      13,13,31,135,131,129,": ",0
+prompt7:   DB      31,228,251," key: ",0
+prompt8:   DB      13,13,31,136,128,131,129," (",0
+prompt9:   DB      13,13,31,130,31,253,0
+prompt9b:  DB      31,129,32,32,32,32,32,31,201,14
+prompt10:  DB      ") ",31,135,31,178,": ",0
+beg_str:   DB      "First",31,129,": ",0
+end_str:   DB      "Last",31,129,":",32,32,0
+sure:      DB      31,185,161," sure?",0
+edits1:    DB      13,13,31,156,154,146,",",140,128,200,14
+edits2:    DB      "  ",31,156,193,",",142,129,247,13,14
+dnlds1:    DB      13,13,31,159," ascii",249,150,31,152,132,137
+           DB      ",",149,140,128,160,13,14
+dnlds2:    DB      13,31,138,160,"ed",13,14
+dnlds3:    DB      13,31,138,193,"d",13,14
+dnlds4:    DB      "Summary:",14
+dnlds5:    DB      " ",198,"s",145,"d",14
+dnlds6a:   DB      " ",139,145,"d",14
+dnlds6b:   DB      " ",139," written",14
+dnlds7:    DB      31,155,":",14
+dnlds8:    DB      " ",139," unable",128," write",14
+dnlds9:    DB      32,32,"bad",245,"s",14
+dnlds10:   DB      " ",133,159,150,198,14
+dnlds11:   DB      " ",133,132,157,14
+dnlds12:   DB      " ",133," non",132,157,14
+dnlds13:   DB      31,151,155," detected",13,14
+runs1:     DB      13,134,"ning",130,":",13,14
+uplds3:    DB      13,13,"Sending",31,152,132,137,172,32,32,0
+uplds4:    DB      " ",128,32,32,0
+help1txt:  DB      13,13,"Standard",31,158,"s",14
+help2txt:  DB      31,218,31,244,"ed",31,158,"s",14
+type1:     DB      31,154,158,0
+type1a:    DB      31,223,0
+type1b:    DB      31,143,31,226,31,170,0
+type2:     DB      31,130,0
+type3:     DB      "Reserved",0
+type4:     DB      31,239,137,0
+type5:     DB      "???",0
+help_cmd:  DB      31,142,215,209,0
+help_cmd2: DB      31,215,0
+dir_cmd:   DB      31,209,130,"s",0
+run_cmd:   DB      31,134,130,0
+dnld_cmd:  DB      31,138,0
+upld_cmd:  DB      31,147,0
+nloc_cmd:  DB      31,135,129,0
+jump_cmd:  DB      31,136,128,131,129,0
+dump_cmd:  DB  31,132,219,154," code",131,0
+xdump_cmd: db  31,132,219,154," data",131,0
+intm_cmd:  DB      31,132,219,192,131,0
+edit_cmd:  DB      31,156,154,146,0
+clrm_cmd:  DB      31,237,131,0
+erfr_cmd:  DB      31,203,153,144,0
+
+
+
+
+;this code attempts to switch from development mode to run mode
+;on the xa_tiny (8-bit data bus) development board.
+
+       org     0x7000
+
+es      sfr     442h
+pswh    sfr     401h
+
+       ; a bunch of nops, to hopefully flush the xa prefetch buffer
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       ;make a read from location 0x0EF000 to switch modes
+       mov.b   es, #0x0E
+       mov     r0, #0xF000
+       setb    ssel.0
+       mov.b   r4l, [r0]       ;make the mode switch
+       mov.b   r4l, [r0]       ;make the mode switch
+       mov.b   r4l, [r0]       ;make the mode switch
+       mov.b   r4l, [r0]       ;make the mode switch
+
+       ;now read the start vector from location zero
+       movs.b  es, #0
+       movs    r0, #0
+       movc    r1l, [r0+]      ;read psw value into r1
+       movc    r1h, [r0+]
+       movc    r2l, [r0+]      ;read start address into r2
+       movc    r2h, [r0+]
+
+       movs.b  ssel, #0
+       mov.b   pswl, r1l
+       mov.b   pswh, r1h
+       jmp     [r2]
+
+
+
diff --git a/as/xa51/todo b/as/xa51/todo
new file mode 100644 (file)
index 0000000..91f3496
--- /dev/null
@@ -0,0 +1,7 @@
+add flag to all symbol structure to remember if it was a line label...
+if a symbol which was a line label is used in immediate addressing,
+it must be considered a branch target.
+-- maybe adding an align directive would be enough...
+
+write error messages into list file
+
diff --git a/as/xa51/xa_asm.l b/as/xa51/xa_asm.l
new file mode 100644 (file)
index 0000000..15b5e93
--- /dev/null
@@ -0,0 +1,307 @@
+%{
+/* This file is part of Paul's XA51 Assembler, Copyright 1997,2002 Paul Stoffregen
+ *
+ * Paul's XA51 Assembler is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ *
+ * Foobar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Author contact: paul@pjrc.com */
+
+#include <stdio.h>
+#include <string.h>
+#include "xa_main.h"
+#include "xa_asm.tab.h"
+
+extern int inst_size, yylval;
+char line_text[MAX_LINE]={'\0'}, last_line_text[MAX_LINE];
+char lex_sym_name[MAX_SYMBOL];
+extern char base_symbol_name[];
+#define LIST if (p3) strcat(line_text, yytext)
+%}
+
+%%
+
+add\.w         {LIST; inst_size=SIZE16; return ADD;}
+add\.b         {LIST; inst_size=SIZE8; return ADD;}
+add            {LIST; inst_size=UNKNOWN; return ADD;}
+addc\.w                {LIST; inst_size=SIZE16; return ADDC;}
+addc\.b                {LIST; inst_size=SIZE8; return ADDC;}
+addc           {LIST; inst_size=UNKNOWN; return ADDC;}
+adds\.w                {LIST; inst_size=SIZE16; return ADDS;}
+adds\.b                {LIST; inst_size=SIZE8; return ADDS;}
+adds           {LIST; inst_size=UNKNOWN; return ADDS;}
+and\.w         {LIST; inst_size=SIZE16; return AND;}
+and\.b         {LIST; inst_size=SIZE8; return AND;}
+and            {LIST; inst_size=UNKNOWN; return AND;}
+anl            {LIST; inst_size=UNKNOWN; return ANL;}
+asl\.d         {LIST; inst_size=SIZE32; return ASL;}
+asl\.w         {LIST; inst_size=SIZE16; return ASL;}
+asl\.b         {LIST; inst_size=SIZE8; return ASL;}
+asl            {LIST; inst_size=UNKNOWN; return ASL;}
+asr\.d         {LIST; inst_size=SIZE32; return ASR;}
+asr\.w         {LIST; inst_size=SIZE16; return ASR;}
+asr\.b         {LIST; inst_size=SIZE8; return ASR;}
+asr            {LIST; inst_size=UNKNOWN; return ASR;}
+bcc            {LIST; inst_size=UNKNOWN; return BCC;}
+bcs            {LIST; inst_size=UNKNOWN; return BCS;}
+beq            {LIST; inst_size=UNKNOWN; return BEQ;}
+bg             {LIST; inst_size=UNKNOWN; return BG;}
+bge            {LIST; inst_size=UNKNOWN; return BGE;}
+bgt            {LIST; inst_size=UNKNOWN; return BGT;}
+bkpt           {LIST; inst_size=UNKNOWN; return BKPT;}
+bl             {LIST; inst_size=UNKNOWN; return BL;}
+ble            {LIST; inst_size=UNKNOWN; return BLE;}
+blt            {LIST; inst_size=UNKNOWN; return BLT;}
+bmi            {LIST; inst_size=UNKNOWN; return BMI;}
+bne            {LIST; inst_size=UNKNOWN; return BNE;}
+bnv            {LIST; inst_size=UNKNOWN; return BNV;}
+bov            {LIST; inst_size=UNKNOWN; return BOV;}
+bpl            {LIST; inst_size=UNKNOWN; return BPL;}
+br             {LIST; inst_size=UNKNOWN; return BR;}
+call           {LIST; inst_size=UNKNOWN; return CALL;}
+cjne\.w                {LIST; inst_size=SIZE16; return CJNE;}
+cjne\.b                {LIST; inst_size=SIZE8; return CJNE;}
+cjne           {LIST; inst_size=UNKNOWN; return CJNE;}
+clr            {LIST; inst_size=UNKNOWN; return CLR;}
+cmp\.w         {LIST; inst_size=SIZE16; return CMP;}
+cmp\.b         {LIST; inst_size=SIZE8; return CMP;}
+cmp            {LIST; inst_size=UNKNOWN; return CMP;}
+cpl\.w         {LIST; inst_size=SIZE16; return CPL;}
+cpl\.b         {LIST; inst_size=SIZE8; return CPL;}
+cpl            {LIST; inst_size=UNKNOWN; return CPL;}
+da\.b          {LIST; inst_size=SIZE8; return DA;}
+da             {LIST; inst_size=UNKNOWN; return DA;}
+div\.d         {LIST; inst_size=SIZE32; return DIV;}
+div\.w         {LIST; inst_size=SIZE16; return DIV;}
+div\.b         {LIST; inst_size=SIZE8; return DIV;}
+div            {LIST; inst_size=UNKNOWN; return DIV;}
+divu\.d                {LIST; inst_size=SIZE32; return DIVU;}
+divu\.w                {LIST; inst_size=SIZE16; return DIVU;}
+divu\.b                {LIST; inst_size=SIZE8; return DIVU;}
+divu           {LIST; inst_size=UNKNOWN; return DIVU;}
+djnz\.w                {LIST; inst_size=SIZE16; return DJNZ;}
+djnz\.b                {LIST; inst_size=SIZE8; return DJNZ;}
+djnz           {LIST; inst_size=UNKNOWN; return DJNZ;}
+fcall          {LIST; inst_size=UNKNOWN; return FCALL;}
+fjmp           {LIST; inst_size=UNKNOWN; return FJMP;}
+jb             {LIST; inst_size=UNKNOWN; return JB;}
+jbc            {LIST; inst_size=UNKNOWN; return JBC;}
+jmp            {LIST; inst_size=UNKNOWN; return JMP;}
+jnb            {LIST; inst_size=UNKNOWN; return JNB;}
+jnz            {LIST; inst_size=UNKNOWN; return JNZ;}
+jz             {LIST; inst_size=UNKNOWN; return JZ;}
+lea\.w         {LIST; inst_size=SIZE16; return LEA;}
+lea            {LIST; inst_size=UNKNOWN; return LEA;}
+lsr\.d         {LIST; inst_size=SIZE32; return LSR;}
+lsr\.w         {LIST; inst_size=SIZE16; return LSR;}
+lsr\.b         {LIST; inst_size=SIZE8; return LSR;}
+lsr            {LIST; inst_size=UNKNOWN; return LSR;}
+mov\.w         {LIST; inst_size=SIZE16; return MOV;}
+mov\.b         {LIST; inst_size=SIZE8; return MOV;}
+mov            {LIST; inst_size=UNKNOWN; return MOV;}
+movc\.w                {LIST; inst_size=SIZE16; return MOVC;}
+movc\.b                {LIST; inst_size=SIZE8; return MOVC;}
+movc           {LIST; inst_size=UNKNOWN; return MOVC;}
+movs\.w                {LIST; inst_size=SIZE16; return MOVS;}
+movs\.b                {LIST; inst_size=SIZE8; return MOVS;}
+movs           {LIST; inst_size=UNKNOWN; return MOVS;}
+movx\.w                {LIST; inst_size=SIZE16; return MOVX;}
+movx\.b                {LIST; inst_size=SIZE8; return MOVX;}
+movx           {LIST; inst_size=UNKNOWN; return MOVX;}
+mul\.d         {LIST; inst_size=SIZE32; return MUL;}
+mul\.w         {LIST; inst_size=SIZE16; return MUL;}
+mul\.b         {LIST; inst_size=SIZE8; return MUL;}
+mul            {LIST; inst_size=UNKNOWN; return MUL;}
+mulu\.d                {LIST; inst_size=SIZE32; return MULU;}
+mulu\.w                {LIST; inst_size=SIZE16; return MULU;}
+mulu\.b                {LIST; inst_size=SIZE8; return MULU;}
+mulu           {LIST; inst_size=UNKNOWN; return MULU;}
+neg\.w         {LIST; inst_size=SIZE16; return NEG;}
+neg\.b         {LIST; inst_size=SIZE8; return NEG;}
+neg            {LIST; inst_size=UNKNOWN; return NEG;}
+nop            {LIST; inst_size=UNKNOWN; return NOP;}
+norm\.d                {LIST; inst_size=SIZE32; return NORM;}
+norm\.w                {LIST; inst_size=SIZE16; return NORM;}
+norm\.b                {LIST; inst_size=SIZE8; return NORM;}
+norm           {LIST; inst_size=UNKNOWN; return NORM;}
+or\.w          {LIST; inst_size=SIZE16; return OR;}
+or\.b          {LIST; inst_size=SIZE8; return OR;}
+or             {LIST; inst_size=UNKNOWN; return OR;}
+orl            {LIST; inst_size=UNKNOWN; return ORL;}
+pop\.w         {LIST; inst_size=SIZE16; return POP;}
+pop\.b         {LIST; inst_size=SIZE8; return POP;}
+pop            {LIST; inst_size=UNKNOWN; return POP;}
+popu\.w                {LIST; inst_size=SIZE16; return POPU;}
+popu\.b                {LIST; inst_size=SIZE8; return POPU;}
+popu           {LIST; inst_size=UNKNOWN; return POPU;}
+push\.w                {LIST; inst_size=SIZE16; return PUSH;}
+push\.b                {LIST; inst_size=SIZE8; return PUSH;}
+push           {LIST; inst_size=UNKNOWN; return PUSH;}
+pushu\.w       {LIST; inst_size=SIZE16; return PUSHU;}
+pushu\.b       {LIST; inst_size=SIZE8; return PUSHU;}
+pushu          {LIST; inst_size=UNKNOWN; return PUSHU;}
+reset          {LIST; inst_size=UNKNOWN; return RESET;}
+ret            {LIST; inst_size=UNKNOWN; return RET;}
+reti           {LIST; inst_size=UNKNOWN; return RETI;}
+rl\.w          {LIST; inst_size=SIZE16; return RL;}
+rl\.b          {LIST; inst_size=SIZE8; return RL;}
+rl             {LIST; inst_size=UNKNOWN; return RL;}
+rlc\.w         {LIST; inst_size=SIZE16; return RLC;}
+rlc\.b         {LIST; inst_size=SIZE8; return RLC;}
+rlc            {LIST; inst_size=UNKNOWN; return RLC;}
+rr\.w          {LIST; inst_size=SIZE16; return RR;}
+rr\.b          {LIST; inst_size=SIZE8; return RR;}
+rr             {LIST; inst_size=UNKNOWN; return RR;}
+rrc\.w         {LIST; inst_size=SIZE16; return RRC;}
+rrc\.b         {LIST; inst_size=SIZE8; return RRC;}
+rrc            {LIST; inst_size=UNKNOWN; return RRC;}
+setb           {LIST; inst_size=UNKNOWN; return SETB;}
+sext\.w                {LIST; inst_size=SIZE16; return SEXT;}
+sext\.b                {LIST; inst_size=SIZE8; return SEXT;}
+sext           {LIST; inst_size=UNKNOWN; return SEXT;}
+sub\.w         {LIST; inst_size=SIZE16; return SUB;}
+sub\.b         {LIST; inst_size=SIZE8; return SUB;}
+sub            {LIST; inst_size=UNKNOWN; return SUB;}
+subb\.w                {LIST; inst_size=SIZE16; return SUBB;}
+subb\.b                {LIST; inst_size=SIZE8; return SUBB;}
+subb           {LIST; inst_size=UNKNOWN; return SUBB;}
+trap           {LIST; inst_size=UNKNOWN; return TRAP;}
+xch\.w         {LIST; inst_size=SIZE16; return XCH;}
+xch\.b         {LIST; inst_size=SIZE8; return XCH;}
+xch            {LIST; inst_size=UNKNOWN; return XCH;}
+xor\.w         {LIST; inst_size=SIZE16; return XOR;}
+xor\.b         {LIST; inst_size=SIZE8; return XOR;}
+xor            {LIST; inst_size=UNKNOWN; return XOR;}
+
+
+dptr           {LIST; return DPTR;}
+pc             {LIST; return PC;}
+a              {LIST; return A;}
+c              {LIST; return C;}
+usp            {LIST; return USP;}
+
+org            {LIST; return ORG;}
+equ            {LIST; return EQU;}
+sfr            {LIST; return EQU;}
+db             {LIST; return DB;}
+dw             {LIST; return DW;}
+byte           {LIST; return DB;}
+bit            {LIST; return BITDEF;}
+reg            {LIST; return REGDEF;}
+low            {LIST; return LOW;}
+high           {LIST; return HIGH;}
+>>             {LIST; return RSHIFT;}
+\<\<           {LIST; return LSHIFT;}
+
+
+R[0-9]         {LIST; yylval = yytext[1] - '0' + WORD_REG; return REG;}
+R1[0-5]                {LIST; yylval = yytext[2] - '0' + 10 + WORD_REG; return REG;}
+R[0-7]L                {LIST; yylval = (yytext[1] - '0') * 2 + BYTE_REG; return REG;}
+R[0-7]H                {LIST; yylval = (yytext[1] - '0') * 2 + 1 + BYTE_REG; return REG;}
+
+
+[a-z_][a-z0-9_]*       {
+                               LIST;
+                               if (is_def(yytext)) {
+                                       yylval = get_value(yytext);
+                               } else {
+                                       yylval = 0;
+                                       if (p3) error("Symbol undefined");
+                               }
+                               /* keep name in lex_sym_name since yytext */
+                               /* could be overwritten if the parser does */
+                               /* a lookahead operation */
+                               strcpy(lex_sym_name, yytext);
+                               if (is_def(lex_sym_name)) {
+                                       yylval = get_value(lex_sym_name);
+                                       /* return correct type if special */
+                                       if (is_bit(lex_sym_name)) return BIT;
+                                       if (is_reg(lex_sym_name)) return REG;
+                               }
+                               return WORD;
+                       }
+[0-9]+\$               {
+                               LIST;
+                               /* should print error is base_symbol_name */
+                               /* is not defined */
+                               sprintf(lex_sym_name, "%s:%s",
+                                       base_symbol_name, yytext);
+                               if (is_def(lex_sym_name)) {
+                                       yylval = get_value(lex_sym_name);
+                               } else {
+                                       yylval = 0;
+                                       if (p3) error("Symbol undefined");
+                               }
+                               return WORD;
+                       }
+[01]+[bq]              {
+                               LIST;
+                               yylval = binary2int(yytext);
+                               return NUMBER;
+                       }
+0x[0-9a-f]+            {
+                               LIST;
+                               sscanf(yytext, "%*c%*c%x", &yylval);
+                               return NUMBER;
+                       }
+[0-9a-f]+[h]           {
+                               LIST;
+                               sscanf(yytext, "%x%*[hH]", &yylval);
+                               return NUMBER;
+                       }
+\$[0-9a-f]+            {
+                               LIST;
+                               sscanf(yytext, "$%x", &yylval);
+                               return NUMBER;
+                       }
+-?[0-9]+               {
+                               LIST;
+                               sscanf(yytext, "%d", &yylval);
+                               return NUMBER;
+                       }
+\'.\'                  {
+                               LIST;
+                               yylval = (int)yytext[1];
+                               return CHAR;
+                       }
+\'\\.\'                        {
+                               LIST;
+                               switch (yytext[1]) {
+                                       case 'n':
+                                       case 'N': yylval = 10; break;
+                                       case 'r':
+                                       case 'R': yylval = 13; break;
+                                       case '0': yylval = 0; break;
+                                       default:  yylval = (int)yytext[1];
+                                       /* print a warning here */
+                               }
+                               return CHAR;
+                       }
+\"[^"\n]*["\n]         {
+                               LIST;
+                               return STRING;
+                       }
+
+;[^\n]*                {LIST; /* comments */}
+[ \t\r]                {LIST; /* whitespace */}
+
+\n             {
+                       strcpy(last_line_text, line_text);
+                       line_text[0] = '\0';
+                       ++lineno;
+                       return EOL;
+               }
+
+.              {LIST; return yytext[0];}
+
+%%
diff --git a/as/xa51/xa_asm.y b/as/xa51/xa_asm.y
new file mode 100644 (file)
index 0000000..aa18e0a
--- /dev/null
@@ -0,0 +1,1283 @@
+%{
+/* This file is part of Paul's XA51 Assembler, Copyright 1997,2002 Paul Stoffregen
+ *
+ * Paul's XA51 Assembler is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ *
+ * Foobar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Author contact: paul@pjrc.com */
+
+/* parser for the 51-XA assembler, Paul Stoffregen, July 1997 */
+#include <stdio.h>
+
+#include "xa_main.h"
+
+int op[MAX_DB];
+int size;
+int inst_size;
+int arith_opcode, short_opcode, num_op, opcode0, opcode1;
+int shift_imm_opcode, shift_reg_opcode, rotate_opcode;
+int stack_addr_opcode, stack_reg_opcode, branch_opcode;
+int rlist_reg_bank, rlist_bitmask, rlist_size;
+int db_count, dw_count, i;
+char symbol_name[MAX_SYMBOL], base_symbol_name[MAX_SYMBOL]={'\0'};
+char expr_var[MAX_SYMBOL]={0,0,0,'\0'};
+
+extern char lex_sym_name[];
+extern int yylex();
+
+extern void yyrestart(FILE *new_file);
+extern char * disasm(int byte, int memory_location);
+extern void hexout(int byte, int memory_location, int end);
+void error(char *s);
+void fatal_error(char *s);
+
+static int bitmask[]={1, 2, 4, 8, 16, 32, 64, 128};
+
+%}
+
+%token ADD ADDC ADDS AND ANL ASL ASR BCC BCS BEQ BG BGE BGT
+%token BKPT BL BLE BLT BMI BNE BNV BOV BPL BR CALL CJNE CLR
+%token CMP CPL DA DIV DIVU DJNZ FCALL FJMP JB JBC JMP JNB JNZ
+%token JZ LEA LSR MOV MOVC MOVS MOVX MUL MULU NEG NOP NORM
+%token OR ORL POP POPU PUSH PUSHU RESET RET RETI RL RLC RR RRC
+%token SETB SEXT SUB SUBB TRAP XCH XOR
+%token REG DPTR PC A C USP
+%token WORD BIT NUMBER CHAR STRING EOL LOCAL_LABEL
+%token ORG EQU DB DW BITDEF REGDEF LOW HIGH
+%token RSHIFT LSHIFT
+
+%left '&' '|' '^'
+%left RSHIFT LSHIFT
+%left '+' '-'
+%left '*' '/'
+%nonassoc UNARY
+
+%%
+
+all:           line
+             | line all;
+
+line:          linesymbol ':' linenosym {
+                       if (p1) build_sym_list(symbol_name);
+                       if (p2) assign_value(symbol_name, mem);
+                       mem += $3; }
+             | linenosym {mem += $1;}
+
+linenosym:     directive EOL {
+                       if (p3) out(op, $1);
+                       $$ = $1;
+               }
+             | instruction EOL {
+                       if (p3) out(op, $1);
+                       $$ = $1;
+               }
+            | EOL {
+                       if (p3) out(NULL, 0);
+                       $$ = 0;
+               }
+            | error EOL        /* try to recover from any parse error */
+
+directive:     '.' ORG expr {
+                       mem = $3;
+                       $$ = 0;
+               }
+            | ORG expr {
+                       mem = $2;
+                       $$ = 0;
+               }
+             | '.' EQU symbol ',' expr { 
+                       if (p1) build_sym_list(symbol_name);
+                       if (p1 || p2) assign_value(symbol_name, $5);
+                       $$ = 0;
+               }
+            | symbol EQU expr {
+                       if (p1) build_sym_list(symbol_name);
+                       if (p1 || p2) assign_value(symbol_name, $3);
+                       $$ = 0;
+               }
+            | '.' BITDEF bitsymbol ',' bit {
+                       if (p1) {
+                               build_sym_list(symbol_name);
+                               mk_bit(symbol_name);
+                       }
+                       if (p1 || p2) assign_value(symbol_name, $5);
+                       $$ = 0;
+               }
+            | bitsymbol BITDEF bit {
+                       if (p1) {
+                                build_sym_list(symbol_name);
+                                mk_bit(symbol_name);
+                        }
+                        if (p1 || p2) assign_value(symbol_name, $3);
+                        $$ = 0;
+                }
+            | '.' REGDEF regsymbol ',' REG {
+                       if (p1) {
+                               build_sym_list(symbol_name);
+                               mk_reg(symbol_name);
+                       }
+                       if (p1 || p2) assign_value(symbol_name, $5);
+                       $$ = 0;
+               }
+            | regsymbol REGDEF REG {
+                       if (p1) {
+                                build_sym_list(symbol_name);
+                                mk_reg(symbol_name);
+                        }
+                        if (p1 || p2) assign_value(symbol_name, $3);
+                        $$ = 0;
+                }
+
+             | db_directive bytes {
+                       $$ = db_count;
+               }
+            | dw_directive words {
+                       $$ = dw_count;
+               }
+
+db_directive:  DB {db_count = 0;}
+
+bytes:           byte_element
+               | bytes ',' byte_element
+
+byte_element:  expr {
+                       op[db_count] = $1 & 255;
+                       if (++db_count >= MAX_DB) {
+                               error("too many bytes, use two DB");
+                               db_count--;
+                       }
+               }
+               | STRING {
+                       for(i=1; i < strlen(yytext)-1; i++) {
+                               op[db_count++] = yytext[i];
+                               if (db_count >= MAX_DB) {
+                                       error("too many bytes, use two DB");
+                                       db_count--;
+                               }
+                       }
+               }
+
+dw_directive:  DW {dw_count = 0;}
+
+words:           words ',' word_element
+               | word_element
+
+word_element:  expr {
+                       op[dw_count] = $1 & 255;
+                       op[dw_count+1] = ($1 >> 8) & 255;
+                       dw_count += 2;
+                       if (dw_count >= MAX_DB) {
+                               error("too many bytes, use two DW");
+                               db_count -= 2;
+                       }
+               }
+
+
+linesymbol:    WORD  { 
+                       strcpy(symbol_name, lex_sym_name);
+                       if (!strchr(lex_sym_name, ':')) {
+                               /* non-local label, remember base name */
+                               strcpy(base_symbol_name, lex_sym_name);
+                       }
+                       if (is_target(symbol_name)) pad_with_nop();
+               }
+
+symbol:     WORD  {
+               strcpy(symbol_name, lex_sym_name);
+               }
+
+bitsymbol:    WORD { strcpy(symbol_name, lex_sym_name); }
+           | BIT  { strcpy(symbol_name, lex_sym_name); }
+
+
+regsymbol:    WORD { strcpy(symbol_name, lex_sym_name); }
+           | REG  { strcpy(symbol_name, lex_sym_name); }
+
+bit:   expr '.' expr {
+               if ($3 < 0 || $3 > 7) {
+                       /* only 8 bits in a byte */
+                       error("Only eight bits in a byte");
+               }
+               $$ = 100000;    /* should really check $1 is valid */
+               if ($1 >= 0x20 && $1 <= 0x3F) {
+                       $$ = $1 * 8 + $3;
+               }
+               if ($1 >= 0x400 && $1 <= 0x43F) {
+                       $$ = ($1 - 0x400) * 8 + $3 + 0x200;
+               }
+       }
+       | REG '.' expr {
+               $$ = 100000;
+               if (find_size_reg($1) == SIZE8) {
+                       if ($3 < 0 || $3 > 7)
+                               error("byte reg has only 8 bits");
+                       $$ = reg($1) * 8 + $3;
+               }
+               if (find_size_reg($1) == SIZE16) {
+                       if ($3 < 0 || $3 > 15)
+                               error("word reg has only 16 bits");
+                       $$ = reg($1) * 16 + $3;
+               }
+       }
+       | BIT {$$ = $1;}
+
+jmpaddr:       WORD {
+                       $$ = $1;
+                       if (p1) build_target_list(lex_sym_name);
+               }
+             | NUMBER {
+                       if ($1 & 1) error("Jump target must be aligned");
+                       $$ = $1;
+               }
+
+
+expr:  value   {$$ = $1;}
+       | expr '+' expr {$$ = $1 + $3;}
+       | expr '-' expr {$$ = $1 - $3;}
+       | expr '*' expr {$$ = $1 * $3;}
+       | expr '/' expr {$$ = $1 / $3;}
+       | expr '&' expr {$$ = $1 & $3;}
+       | expr '|' expr {$$ = $1 | $3;}
+       | expr '^' expr {$$ = $1 ^ $3;}
+       | expr RSHIFT expr {$$ = $1 >> $3;}
+       | expr LSHIFT expr {$$ = $1 << $3;}
+       | '-' expr %prec UNARY {$$ = $2 * -1;}
+       | '+' expr %prec UNARY {$$ = $2;}
+       | '(' expr ')' {$$ = $2;}
+       | LOW expr %prec UNARY {$$ = $2 & 255;}
+       | HIGH expr %prec UNARY {$$ = ($2 >> 8) & 255;}
+
+
+value:   NUMBER {$$ = $1;}
+       | CHAR {$$ = $1;}
+       | WORD {$$ = $1; strcpy(expr_var, yytext);}
+
+
+rlist: REG {
+               rlist_bitmask = bitmask[reg($1) % 8];
+               rlist_reg_bank = (reg($1) / 8) ? 1 : 0;
+               rlist_size = find_size_reg($1);
+       }
+       | REG ',' rlist {
+               rlist_bitmask |= bitmask[reg($1) % 8];
+               if (rlist_reg_bank != ((reg($1) / 8) ? 1 : 0))
+                       error("register list may not mix 0-7/8-15 regs");
+               if (rlist_size != find_size_reg($1))
+                       error("register list may not mix 8/16 bit registers");
+       }
+
+
+
+
+
+instruction:
+
+  arith_inst REG ',' REG {
+       $$ = 2;
+       size = find_size2(inst_size, $2, $4);
+       op[0] = arith_opcode * 16 + size * 8 + 1;
+       op[1] = reg($2) * 16 + reg($4);
+  }
+| arith_inst REG ',' '[' REG ']' {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = arith_opcode * 16 + size * 8 + 2;
+       op[1] = reg($2) * 16 + reg_indirect($5);
+  }
+| arith_inst '[' REG ']' ',' REG {
+       $$ = 2;
+       size = find_size1(inst_size, $6);
+       op[0] = arith_opcode * 16 + size * 8 + 2;
+       op[1] = reg($6) * 16 + 8 + reg_indirect($3);
+  }
+| arith_inst REG ',' '[' REG '+' expr ']' {
+       size = find_size1(inst_size, $2);
+       if ($7 >= -128 && $7 <= 127) {
+               $$ = 3;
+               op[0] = arith_opcode * 16 + size * 8 + 4;
+               op[1] = reg($2) * 16 + reg_indirect($5);
+               op[2] = ($7 >= 0) ? $7 : 256 + $7;
+       } else {
+               $$ = 4;
+               op[0] = arith_opcode * 16 + size * 8 + 5;
+               op[1] = reg($2) * 16 + reg_indirect($5);
+               op[2] = ($7 >= 0) ? msb($7) : msb(65536 + $7);
+               op[3] = ($7 >= 0) ? lsb($7) : lsb(65536 + $7);
+       }
+  }
+| arith_inst '[' REG '+' expr ']' ',' REG {
+       size = find_size1(inst_size, $8);
+       if ($5 >= -128 && $5 <= 127) {
+               $$ = 3;
+               op[0] = arith_opcode * 16 + size * 8 + 4;
+               op[1] = reg($8) * 16 + 8 + reg_indirect($3);
+               op[2] = ($5 >= 0) ? $5 : 256 + $5;
+       } else {
+               $$ = 4;
+               op[0] = arith_opcode * 16 + size * 8 + 5;
+               op[1] = reg($8) * 16 + 8 + reg_indirect($3);
+               op[2] = ($5 >= 0) ? msb($5) : msb(65536 + $5);
+               op[3] = ($5 >= 0) ? lsb($5) : lsb(65536 + $5);
+       }
+  }
+| arith_inst REG ',' '[' REG '+' ']' {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = arith_opcode * 16 + size * 8 + 3;
+       op[1] = reg($2) * 16 + reg_indirect($5);
+  }
+| arith_inst '[' REG '+' ']' ',' REG {
+       $$ = 2;
+       size = find_size1(inst_size, $7);
+       op[0] = arith_opcode * 16 + size * 8 + 3;
+       op[1] = reg($7) * 16 + 8 + reg_indirect($3);
+  }
+| arith_inst expr ',' REG {
+       $$ = 3;
+       size = find_size1(inst_size, $4);
+       op[0] = arith_opcode * 16 + size * 8 + 6;
+       op[1] = reg($4) * 16 + 8 + msb(direct_addr($2));
+       op[2] = lsb(direct_addr($2));
+  }
+| arith_inst REG ',' expr {
+       $$ = 3;
+       size = find_size1(inst_size, $2);
+       op[0] = arith_opcode * 16 + size * 8 + 6;
+       op[1] = reg($2) * 16 + msb(direct_addr($4));
+       op[2] = lsb(direct_addr($4));
+  }
+| arith_inst REG ',' '#' expr {
+       size = find_size1(inst_size, $2);
+       if (size == SIZE8) {
+               $$ = 3;
+               op[0] = 0x91;
+               op[1] = reg($2) * 16 + arith_opcode;
+               op[2] = imm_data8($5);
+       } else {
+               $$ = 4;
+               op[0] = 0x99;
+               op[1] = reg($2) * 16 + arith_opcode;
+               op[2] = msb(imm_data16($5));
+               op[3] = lsb(imm_data16($5));
+       }
+  }
+| arith_inst '[' REG ']' ',' '#' expr {
+       size = find_size0(inst_size);
+       if (size == SIZE8) {
+               $$ = 3;
+               op[0] = 0x92;
+               op[1] = reg_indirect($3) * 16 + arith_opcode;
+               op[2] = imm_data8($7);
+       } else {
+               $$ = 4;
+               op[0] = 0x9A;
+               op[1] = reg_indirect($3) * 16 + arith_opcode;
+               op[2] = msb(imm_data16($7));
+               op[3] = lsb(imm_data16($7));
+       }
+  }
+| arith_inst '[' REG '+' ']' ',' '#' expr {
+       size = find_size0(inst_size);
+       if (size == SIZE8) {
+               $$ = 3;
+               op[0] = 0x93;
+               op[1] = reg_indirect($3) * 16 + arith_opcode;
+               op[2] = imm_data8($8);
+       } else {
+               $$ = 4;
+               op[0] = 0x9B;
+               op[1] = reg_indirect($3) * 16 + arith_opcode;
+               op[2] = msb(imm_data16($8));
+               op[3] = lsb(imm_data16($8));
+       }
+  }
+| arith_inst '[' REG '+' expr ']' ',' '#' expr {
+       size = find_size0(inst_size);
+       if ($5 >= -128 && $5 <= 127) {
+               if (size == SIZE8) {
+                       $$ = 4;
+                       op[0] = 0x94;
+                       op[1] = reg_indirect($3) * 16 + arith_opcode;
+                       op[2] = ($5 >= 0) ? $5 : 256 + $5;
+                       op[3] = imm_data8($9);
+               } else {
+                       $$ = 5;
+                       op[0] = 0x9C;
+                       op[1] = reg_indirect($3) * 16 + arith_opcode;
+                       op[2] = ($5 >= 0) ? $5 : 256 + $5;
+                       op[3] = msb(imm_data16($9));
+                       op[4] = lsb(imm_data16($9));
+               }
+       } else {
+               if (size == SIZE8) {
+                       $$ = 5;
+                       op[0] = 0x95;
+                       op[1] = reg_indirect($3) * 16 + arith_opcode;
+                       op[2] = ($5 >= 0) ? msb($5) : msb(65536 + $5);
+                       op[3] = ($5 >= 0) ? lsb($5) : lsb(65536 + $5);
+                       op[4] = imm_data8($9);
+               } else {
+                       $$ = 6;
+                       op[0] = 0x9D;
+                       op[1] = reg_indirect($3) * 16 + arith_opcode;
+                       op[2] = ($5 >= 0) ? msb($5) : msb(65536 + $5);
+                       op[3] = ($5 >= 0) ? lsb($5) : lsb(65536 + $5);
+                       op[4] = msb(imm_data16($9));
+                       op[5] = lsb(imm_data16($9));
+               }
+       }
+  }
+| arith_inst expr ',' '#' expr {
+       size = find_size0(inst_size);
+       if (size == SIZE8) {
+               $$ = 4;
+               op[0] = 0x96;
+               op[1] = msb(direct_addr($2)) * 16 + arith_opcode;
+               op[2] = lsb(direct_addr($2));
+               op[3] = imm_data8($5);
+       } else {
+               $$ = 5;
+               op[0] = 0x9E;
+               op[1] = msb(direct_addr($2)) * 16 + arith_opcode;
+               op[2] = lsb(direct_addr($2));
+               op[3] = msb(imm_data16($5));
+               op[4] = lsb(imm_data16($5));
+       }
+  }
+
+/* the next 8 instructions are MOV, but because MOV was used in the */
+/* arith_inst group, it will cause a shift/reduce conflict if used */
+/* directly below... so we're forced to use arith_inst and then */
+/* add a bit of code to make sure it was MOV and not the other ones */
+
+| arith_inst '[' REG '+' ']' ',' '[' REG '+' ']' {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (1)");
+       size = find_size0(inst_size);
+       $$ = 2;
+       op[0] = 0x90 + size * 8;
+       op[1] = reg_indirect($3) * 16 + reg_indirect($8);
+  }
+| arith_inst expr ',' '[' REG ']' {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (2)");
+       size = find_size0(inst_size);
+       $$ = 3;
+       op[0] = 0xA0 + size * 8;
+       op[1] = 128 + reg_indirect($5) * 16 + msb(direct_addr($2));
+       op[2] = lsb(direct_addr($2));
+  }
+| arith_inst '[' REG ']' ',' expr {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (3)");
+       size = find_size0(inst_size);
+       $$ = 3;
+       op[0] = 0xA0 + size * 8;
+       op[1] = reg_indirect($3) * 16 + msb(direct_addr($6));
+       op[2] = lsb(direct_addr($6));
+  }
+| arith_inst expr ',' expr {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (4)");
+       size = find_size0(inst_size);
+       $$ = 4;
+       op[0] = 0x97 + size * 8;
+       op[1] = msb(direct_addr($2)) * 16 + msb(direct_addr($4));
+       op[2] = lsb(direct_addr($2));
+       op[3] = lsb(direct_addr($4));
+  }
+| arith_inst REG ',' USP {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (5)");
+       $$ = 2;
+       op[0] = 0x90;
+       op[1] = reg($2) * 16 + 15;
+  }
+| arith_inst USP ',' REG {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (6)");
+       $$ = 2;
+       op[0] = 0x98;
+       op[1] = reg($4) * 16 + 15;
+  }
+| arith_inst C ',' bit {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (7)");
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = 0x20 + msb(bit_addr($4));
+       op[2] = lsb(bit_addr($4));
+  }
+| arith_inst bit ',' C {
+       /* this addr mode is only valid for MOV */
+       if (arith_opcode != 8) error("Addr mode only valid for MOV (8)");
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = 0x30 + msb(bit_addr($2));
+       op[2] = lsb(bit_addr($2));
+  }
+
+| MOVC REG ',' '[' REG '+' ']' {
+       size = find_size1(inst_size, $2);
+       $$ = 2;
+       op[0] = 0x80 + size * 8;
+       op[1] = reg($2) * 16 + reg_indirect($5);
+  }
+| MOVC A ',' '[' A '+' DPTR ']' {
+       $$ = 2;
+       op[0] = 0x90;
+       op[1] = 0x4E;
+  }
+| MOVC A ',' '[' A '+' PC ']' {
+       $$ = 2;
+       op[0] = 0x90;
+       op[1] = 0x4C;
+  }
+| MOVX REG ',' '[' REG ']' {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = 0xA7 + size * 8;
+       op[1] = reg($2) * 16 + reg_indirect($5);
+  }
+| MOVX '[' REG ']' ',' REG {
+       $$ = 2;
+       size = find_size1(inst_size, $6);
+       op[0] = 0xA7 + size * 8;
+       op[1] = reg($6) * 16 + 8 + reg_indirect($3);
+  }
+| XCH REG ',' REG {
+       $$ = 2;
+       size = find_size2(inst_size, $2, $4);
+       op[0] = 0x60 + size * 8;
+       op[1] = reg($2) * 16 + reg($4);
+  }
+| XCH REG ',' '[' REG ']' {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = 0x50 + size * 8;
+       op[1] = reg($2) * 16 + reg_indirect($5);
+  }
+| XCH REG ',' expr {
+       $$ = 3;
+       size = find_size1(inst_size, $2);
+       op[0] = 0xA0 + size * 8;
+       op[1] = reg($2) * 16 + msb(direct_addr($4));
+        op[2] = lsb(direct_addr($4));
+  }
+| short_data_inst REG ',' '#' expr {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = short_opcode + size * 8 + 1;
+       op[1] = reg($2) * 16 + imm_data4_signed($5);
+  }
+| short_data_inst '[' REG ']' ',' '#' expr {
+       $$ = 2;
+       size = find_size0(inst_size);
+       op[0] = short_opcode + size * 8 + 2;
+       op[1] = reg_indirect($3) * 16 + imm_data4_signed($7);
+  }
+| short_data_inst '[' REG '+' ']' ',' '#' expr {
+       $$ = 2;
+       size = find_size0(inst_size);
+       op[0] = short_opcode + size * 8 + 3;
+       op[1] = reg_indirect($3) * 16 + imm_data4_signed($8);
+  }
+| short_data_inst '[' REG '+' expr ']' ',' '#' expr {
+       size = find_size0(inst_size);
+       if ($5 >= -128 && $5 <= 127) {
+               $$ = 3;
+               op[0] = short_opcode + size * 8 + 4;
+               op[1] = reg_indirect($3) * 16 + imm_data4_signed($9);
+               op[2] = op[2] = ($5 >= 0) ? $5 : 256 + $5;
+       } else {
+               $$ = 4;
+               op[0] = short_opcode + size * 8 + 5;
+               op[1] = reg_indirect($3) * 16 + imm_data4_signed($9);
+               op[2] = ($5 >= 0) ? msb($5) : msb(65536 + $5);
+               op[3] = ($5 >= 0) ? lsb($5) : lsb(65536 + $5);
+       }
+  }
+| short_data_inst expr ',' '#' expr {
+       $$ = 3;
+       size = find_size0(inst_size);
+       op[0] = short_opcode + size * 8 + 6;
+       op[1] = msb(direct_addr($2)) * 16 + imm_data4_signed($5);
+       op[2] = lsb(direct_addr($2));
+  }
+
+
+
+| ANL C ',' bit {
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = 0x40 + msb(bit_addr($4));
+       op[2] = lsb(bit_addr($4));
+  }
+| ANL C ',' '/' bit {
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = 0x50 + msb(bit_addr($5));
+       op[2] = lsb(bit_addr($5));
+  }
+
+| ORL C ',' bit {
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = 0x60 + msb(bit_addr($4));
+       op[2] = lsb(bit_addr($4));
+  }
+| ORL C ',' '/' bit {
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = 0x70 + msb(bit_addr($5));
+       op[2] = lsb(bit_addr($5));
+  }
+| CLR bit {
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = msb(bit_addr($2));
+       op[2] = lsb(bit_addr($2));
+  }
+| SETB bit {
+       $$ = 3;
+       op[0] = 0x08;
+       op[1] = 0x10 + msb(bit_addr($2));
+       op[2] = lsb(bit_addr($2));
+  }
+| logical_shift_inst REG ',' REG {
+       size = find_size1(inst_size, $2);
+       if (find_size_reg($4) != SIZE8)
+               error("Second register in logical shift must be byte size");
+       $$ = 2;
+       op[0] = shift_reg_opcode;
+       switch (size) {
+               case SIZE8:  op[0] += 0; break;
+               case SIZE16: op[0] += 8; break;
+               case SIZE32: op[0] += 12; break;
+       }
+       op[1] = reg($2) * 16 + reg($4);
+  }
+| logical_shift_inst REG ',' '#' expr {
+        size = find_size1(inst_size, $2);
+        $$ = 2;
+       if (shift_imm_opcode == -1)
+               error("NORM may not use a constant");
+        op[0] = shift_imm_opcode;
+        switch (size) {
+                case SIZE8:  op[0] += 0; break;
+                case SIZE16: op[0] += 8; break;
+                case SIZE32: op[0] += 12; break;
+        }
+       switch (size) {
+               case SIZE8:
+               case SIZE16:
+                       op[1] = reg($2) * 16 + imm_data4_unsigned($5);
+                       break;
+               case SIZE32:
+                       op[1] = (reg($2) / 2) * 32 + imm_data5_unsigned($5);
+                       break;
+       }
+  }
+| no_opperand_inst {
+       $$ = num_op;
+       op[0] = opcode0;
+       op[1] = opcode1;
+  }
+
+| TRAP '#' expr {
+       $$ = 2;
+       op[0] = 0xD6;
+       op[1] = 0x30 + imm_data4_unsigned($3);
+  }
+| CPL REG {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = 0x90 + size * 8;
+       op[1] = reg($2) * 16 + 10;
+  }
+| DA REG {
+       $$ = 2;
+       op[0] = 0x90;
+       op[1] = reg($2) * 16 + 8;
+  }
+| NEG REG {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = 0x90 + size * 8;
+       op[1] = reg($2) * 16 + 11;
+  }
+| SEXT REG {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = 0x90 + size * 8;
+       op[1] = reg($2) * 16 + 9;
+  }
+
+| rotate_inst REG ',' '#' expr {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = rotate_opcode + size * 8;
+       op[1] = reg($2) * 16 + imm_data4_unsigned($5);
+  }
+
+
+| LEA REG ',' REG '+' expr {
+       if ($6 >= -128 && $6 <= 127) {
+               $$ = 3;
+               op[0] = 0x40;
+               op[1] = reg($2) * 16 + reg_indirect($4);
+               op[2] = ($6 >= 0) ? $6 : 256 + $6;
+       } else {
+               op[0] = 0x48;
+               op[1] = reg($2) * 16 + reg_indirect($4);
+               op[2] = ($6 >= 0) ? msb($6) : msb(65536 + $6);
+               op[3] = ($6 >= 0) ? lsb($6) : lsb(65536 + $6);
+       }
+  }
+| stack_inst expr {
+       $$ = 3;
+       size = find_size0(inst_size);
+       op[0] = msb(stack_addr_opcode) + size * 8;
+       op[1] = lsb(stack_addr_opcode) + msb(direct_addr($2));
+       op[2] = lsb(direct_addr($2));
+  }
+| stack_inst rlist {
+       $$ = 2;
+       if (inst_size != UNKNOWN && find_size0(inst_size) != rlist_size)
+               error("inst specifies different size than registers used");
+       op[0] = stack_reg_opcode + rlist_reg_bank * 64 + rlist_size * 8;
+       op[1] = rlist_bitmask;
+  }
+
+
+| MUL REG ',' REG {
+       $$ = 2;
+       size = find_size2(inst_size, $2, $4);
+       op[0] = 0xE6;
+       op[1] = reg($2) * 16 + reg($4);
+  }
+| MULU REG ',' REG {
+       $$ = 2;
+       size = find_size2(inst_size, $2, $4);
+       if (size == SIZE8) {
+               op[0] = 0xE0;
+               op[1] = reg($2) * 16 + reg($4);
+       } else {
+               op[0] = 0xE4;
+               op[1] = reg($2) * 16 + reg($4);
+       }
+  }
+| MUL REG ',' '#' expr {
+       $$ = 2;
+       size = find_size1(inst_size, $2);
+       op[0] = 0xE9;
+       op[1] = reg($2) + 8;
+       op[2] = msb(imm_data16($5));
+       op[3] = lsb(imm_data16($5));
+  }
+| MULU REG ',' '#' expr {
+       size = find_size2(inst_size, $2, $4);
+       if (size == SIZE8) {
+               $$ = 3;
+               op[0] = 0xE8;
+               op[1] = reg($2) * 16;
+               op[2] = imm_data8($5);
+       } else {
+               $$ = 4;
+               op[0] = 0xE9;
+               op[1] = reg($2) * 16;
+               op[2] = msb(imm_data16($5));
+               op[3] = lsb(imm_data16($5));
+       }
+  }
+| DIV REG ',' REG {
+       $$ = 2;
+       size = find_size2(inst_size, $2, $4);
+       switch (size) {
+       case SIZE8:
+               error("Singed DIV can't be 8 bit size"); break;
+       case SIZE16:
+               op[0] = 0xE7;
+               op[1] = reg($2) * 16 + reg($4);
+               break;
+       case SIZE32:
+               op[0] = 0xEF;
+               op[1] = (reg($2) / 2) * 32 + reg($4);
+               break;
+       }
+  }
+| DIVU REG ',' REG {
+       $$ = 2;
+       size = find_size2(inst_size, $2, $4);
+       switch (size) {
+       case SIZE8:
+               op[0] = 0xE1;
+               op[1] = reg($2) * 16 + reg($4);
+               break;
+       case SIZE16:
+               op[0] = 0xE5;
+               op[1] = reg($2) * 16 + reg($4);
+               break;
+       case SIZE32:
+               op[0] = 0xED;
+               op[1] = (reg($2) / 2) * 32 + reg($4);
+               break;
+       }
+  }
+| DIV REG ',' '#' expr { 
+       size = find_size1(inst_size, $2);
+       switch (size) {
+       case SIZE8:
+               error("Singed DIV can't be 8 bit size"); break;
+       case SIZE16:
+               $$ = 3;
+               op[0] = 0xE8;
+               op[1] = reg($2) * 16 + 11;
+               op[2] = imm_data8($5);
+               break;
+       case SIZE32:
+               $$ = 4;
+               op[0] = 0xE9;
+               op[1] = (reg($2) / 2) * 32 + 9;
+               op[2] = msb(imm_data16($5));
+               op[3] = lsb(imm_data16($5));
+               break;
+       }
+  }
+| DIVU REG ',' '#' expr { 
+       size = find_size1(inst_size, $2);
+       switch (size) {
+       case SIZE8:
+               $$ = 3;
+               op[0] = 0xE8;
+               op[1] = reg($2) * 16 + 1;
+               op[2] = imm_data8($5);
+               break;
+       case SIZE16:
+               $$ = 3;
+               op[0] = 0xE8;
+               op[1] = reg($2) * 16 + 3;
+               op[2] = imm_data8($5);
+               break;
+       case SIZE32:
+               $$ = 4;
+               op[0] = 0xE9;
+               op[1] = (reg($2) / 2) * 32 + 1;
+               op[2] = msb(imm_data16($5));
+               op[3] = lsb(imm_data16($5));
+               break;
+       }
+  }
+| CALL '[' REG ']' {
+       $$ = 2;
+       op[0] = 0xC6;
+       op[1] = reg($3);
+  }
+| FCALL jmpaddr {
+       $$ = 4;
+       op[0] = 0xC4;
+       op[1] = ($2 >> 8) & 255;
+       op[2] = $2 & 255;
+       op[3] = ($2 >> 16) & 255;
+  }
+| FJMP jmpaddr {
+       $$ = 4;
+       op[0] = 0xD4;
+       op[1] = ($2 >> 8) & 255;
+       op[2] = $2 & 255;
+       op[3] = ($2 >> 16) & 255;
+  }
+| JMP '[' REG ']' {
+       $$ = 2;
+       op[0] = 0xD6;
+       op[1] = 0x70 + reg_indirect($3);
+  }
+| JMP '[' A '+' DPTR ']' {
+       $$ = 2;
+       op[0] = 0xD6;
+       op[1] = 0x46;
+  }
+| JMP '[' '[' REG '+' ']' ']' {
+       $$ = 2;
+       op[0] = 0xD6;
+       op[1] = 0x60 + reg_indirect($4);
+  }
+
+| JMP jmpaddr {
+       $$ = 3;
+       op[0] = 0xD5;
+       op[1] = msb(rel16(mem + $$, $2));
+       op[2] = lsb(rel16(mem + $$, $2));
+  }
+
+| CALL jmpaddr {
+        $$ = 3;
+        op[0] = 0xC5;
+        op[1] = msb(rel16(mem + $$, $2));
+        op[2] = lsb(rel16(mem + $$, $2));
+  }
+| branch_inst jmpaddr {
+       $$ = 2;
+       op[0] = branch_opcode;
+       op[1] = rel8(mem + $$, $2);
+  }
+| CJNE REG ',' expr ',' jmpaddr {
+        $$ = 4;
+       size = find_size1(inst_size, $2);
+       op[0] = 0xE2 + size * 8;
+       op[1] = reg($2) * 16 + msb(direct_addr($4));
+       op[2] = lsb(direct_addr($4));
+       op[3] = rel8(mem + $$, $6);
+  }
+| CJNE REG ',' '#' expr ',' jmpaddr {
+       size  = find_size1(inst_size, $2);
+       if (size == SIZE8) {
+               $$ = 4;
+               op[0] = 0xE3;
+               op[1] = reg($2) * 16;
+               op[2] = rel8(mem + $$, $7);
+               op[3] = imm_data8($5);
+       } else {
+               $$ = 5;
+               op[0] = 0xEB;
+               op[1] = reg($2) * 16;
+               op[2] = rel8(mem + $$, $7);
+               op[3] = msb(imm_data16($5));
+               op[4] = lsb(imm_data16($5));
+       }
+  }
+| CJNE '[' REG ']' ',' '#' expr ',' jmpaddr {
+       size  = find_size0(inst_size);
+       if (size == SIZE8) {
+               $$ = 4;
+               op[0] = 0xE3;
+               op[1] = reg_indirect($3) * 16 + 8;
+               op[2] = rel8(mem + $$, $9);
+               op[3] = imm_data8($7);
+       } else {
+               $$ = 5;
+               op[0] = 0xEB;
+               op[1] = reg_indirect($3) * 16 + 8;
+               op[2] = rel8(mem + $$, $9);
+               op[3] = msb(imm_data16($7));
+               op[4] = lsb(imm_data16($7));
+       }
+  }
+| DJNZ REG ',' jmpaddr {
+       $$ = 3;
+       size  = find_size1(inst_size, $2);
+       op[0] = 0x87 + size * 8;
+       op[1] = reg($2) * 16 + 8;
+       op[2] = rel8(mem + $$, $4);
+  }
+
+
+| DJNZ expr ',' jmpaddr {
+       $$ = 4;
+       size  = find_size0(inst_size);
+       op[0] = 0xE2 + size * 8;
+       op[1] = msb(direct_addr($2)) + 8;
+       op[2] = lsb(direct_addr($2));
+       op[3] = rel8(mem + $$, $4);
+  }
+
+| JB bit ',' jmpaddr {
+       $$ = 4;
+       op[0] = 0x97;
+       op[1] = 0x80 + msb(bit_addr($2));
+       op[2] = lsb(bit_addr($2));
+       op[3] = rel8(mem + $$, $4);
+  }
+
+| JBC bit ',' jmpaddr {
+       $$ = 4;
+       op[0] = 0x97;
+       op[1] = 0xC0 + msb(bit_addr($2));
+       op[2] = lsb(bit_addr($2));
+       op[3] = rel8(mem + $$, $4);
+  }
+
+| JNB bit ',' jmpaddr {
+       $$ = 4;
+       op[0] = 0x97;
+       op[1] = 0xA0 + msb(bit_addr($2));
+       op[2] = lsb(bit_addr($2));
+       op[3] = rel8(mem + $$, $4);
+  }
+
+
+arith_inst:
+         ADD   {arith_opcode = 0;}
+       | ADDC  {arith_opcode = 1;}
+       | AND   {arith_opcode = 5;}
+       | CMP   {arith_opcode = 4;}
+       | MOV   {arith_opcode = 8;}
+       | OR    {arith_opcode = 6;}
+       | SUB   {arith_opcode = 2;}
+       | SUBB  {arith_opcode = 3;}
+       | XOR   {arith_opcode = 7;}
+
+short_data_inst:
+         ADDS {short_opcode = 0xA0;}
+       | MOVS {short_opcode = 0xB0;}
+
+logical_shift_inst:
+         ASL  {shift_reg_opcode = 0xC1; shift_imm_opcode = 0xD1;}
+       | ASR  {shift_reg_opcode = 0xC2; shift_imm_opcode = 0xD2;}
+       | LSR  {shift_reg_opcode = 0xC0; shift_imm_opcode = 0xD0;}
+       | NORM {shift_reg_opcode = 0xC3; shift_imm_opcode = -1;}
+
+rotate_inst:
+         RL    {rotate_opcode = 0xD3;}
+       | RLC   {rotate_opcode = 0xD7;}
+       | RR    {rotate_opcode = 0xD0;}
+       | RRC   {rotate_opcode = 0xD7;}
+
+stack_inst:
+         POP   {stack_addr_opcode = 0x8710; stack_reg_opcode = 0x27;}
+       | POPU  {stack_addr_opcode = 0x8700; stack_reg_opcode = 0x37;}
+       | PUSH  {stack_addr_opcode = 0x8730; stack_reg_opcode = 0x07;}
+       | PUSHU {stack_addr_opcode = 0x8720; stack_reg_opcode = 0x17;}
+
+no_opperand_inst:
+         BKPT  {num_op = 1; opcode0 = 255; opcode1 = 0;}
+       | NOP   {num_op = 1; opcode0 = 0; opcode1 = 0;}
+       | RESET {num_op = 2; opcode0 = 0xD6; opcode1 = 0x10;}
+       | RET   {num_op = 2; opcode0 = 0xD6; opcode1 = 0x80;}
+       | RETI  {num_op = 2; opcode0 = 0xD6; opcode1 = 0x90;}
+
+branch_inst:
+         BCC   {branch_opcode = 0xF0;}
+       | BCS   {branch_opcode = 0xF1;}
+       | BEQ   {branch_opcode = 0xF3;}
+       | BG    {branch_opcode = 0xF8;}
+       | BGE   {branch_opcode = 0xFA;}
+       | BGT   {branch_opcode = 0xFC;}
+       | BL    {branch_opcode = 0xF9;}
+       | BLE   {branch_opcode = 0xFD;}
+       | BLT   {branch_opcode = 0xFB;}
+       | BMI   {branch_opcode = 0xF7;}
+       | BNE   {branch_opcode = 0xF2;}
+       | BNV   {branch_opcode = 0xF4;}
+       | BOV   {branch_opcode = 0xF5;}
+       | BPL   {branch_opcode = 0xF6;}
+       | BR    {branch_opcode = 0xFE;}
+       | JZ    {branch_opcode = 0xEC;}
+       | JNZ   {branch_opcode = 0xEE;}
+
+
+
+%%
+
+
+int reg(int reg_spec)
+{
+       return reg_spec & 15;
+}
+
+int reg_indirect(int reg_spec)
+{
+       if (reg_spec & BYTE_REG)
+               error("Imdirect addressing may not use byte registers");
+       if ((reg_spec & 15) > 7)
+               error("Only R0 through R7 may be used for indirect addr");
+       return reg_spec & 7;
+}
+
+int rel16(int pos, int dest)
+{
+       int rel;
+
+       if (!p3) return 0;      /* don't bother unless writing code */
+       if (dest & (BRANCH_SPACING - 1))
+               error("Attempt to jump to unaligned location");
+       pos &= ~(BRANCH_SPACING - 1);
+       rel = (dest - pos) / BRANCH_SPACING;
+       if (rel < -32768 || rel > 32767)
+               error("Attempt to jump out of 16 bit relative range");
+       if (rel < 0) rel += 65536;
+       return rel;
+}
+
+int rel8(int pos, int dest)
+{
+       int rel;
+
+       if (!p3) return 0;      /* don't bother unless writing code */
+       if (dest & (BRANCH_SPACING - 1))
+               error("Attempt to jump to unaligned location");
+       pos &= ~(BRANCH_SPACING - 1);
+       rel = (dest - pos) / BRANCH_SPACING;
+       if (rel < -128 || rel > 127)
+               error("Attempt to jump out of 16 bit relative range");
+       if (rel < 0) rel += 256;
+       return rel;
+}
+
+int msb(int value)
+{
+       return (value >> 8) & 255;
+}
+
+int lsb(int value)
+{
+       return value & 255;
+}
+
+int direct_addr(int value)
+{
+       char buf[250];
+
+       if (value < 0 || value > 2047) {
+               sprintf(buf, "illegal value (%d) for direct address", value);
+               error(buf);
+       }
+       return value;
+}
+
+int imm_data4_signed(int value)
+{
+       if (value < -8 || value > 7)
+               error("illegal 4 bit (signed) value");
+       if (value >= 0) return value;
+       else return (16 + value);
+}
+
+int imm_data4_unsigned(int value)
+{
+       if (value < 0 || value > 15)
+               error("illegal 4 bit (unsigned) value");
+       return value;
+}
+
+int imm_data5_unsigned(int value)
+{
+       if (value < 0 || value > 31)
+               error("illegal 5 bit (unsigned) value");
+       return value;
+}
+
+int imm_data8(int value)
+{
+       if (value < -128 || value > 255)
+               error("illegal 8 bit value");
+       if (value >= 0) return value;
+       else return (256 + value);
+}
+
+int imm_data16(int value)
+{
+       if (value < -32728 || value > 65535)
+               error("illegal 16 bit value");
+       if (value >= 0) return value;
+       else return (65536 + value);
+}
+
+int bit_addr(int value)
+{
+       if (value < 0 || value > 1023) {
+               fprintf(stderr, "bad bit addr of 0x%04X (%d dec)\n",
+                       value, value);
+               error("illegal bit address");
+       }
+       return value;
+}
+
+
+int find_size_reg(int op1spec)
+{
+       int op1size=UNKNOWN;
+
+       if (op1spec & BYTE_REG) op1size = SIZE8;
+       if (op1spec & WORD_REG) op1size = SIZE16;
+       if (op1size == UNKNOWN)
+               error("Register without implied size");
+       return op1size;
+}
+
+int find_size0(int isize)
+{
+       if (isize == UNKNOWN)
+               error("Can't determine data size from instruction");
+       return isize;
+}
+
+int find_size1(int isize, int op1spec)
+{
+       int op1size=UNKNOWN;
+
+       if (op1spec & BYTE_REG) op1size = SIZE8;
+       if (op1spec & WORD_REG) op1size = SIZE16;
+       if (op1size == UNKNOWN)
+               error("Register without implied size");
+
+       if (isize == SIZE32 && op1size == SIZE16) return SIZE32;
+       if (isize == UNKNOWN) return op1size;
+       else {
+               if (isize != op1size)
+                       error("data size of register and inst don't agree");
+               return isize;
+       }
+}
+
+int find_size2(int isize, int op1spec, int op2spec)
+{
+       int op1size=UNKNOWN, op2size=UNKNOWN;
+
+       if (op1spec & BYTE_REG) op1size = SIZE8;
+       if (op1spec & WORD_REG) op1size = SIZE16;
+       if (op1size == UNKNOWN)
+               error("Register without implied size");
+       if (op2spec & BYTE_REG) op2size = SIZE8;
+       if (op2spec & WORD_REG) op2size = SIZE16;
+       if (op1size == UNKNOWN)
+               error("Register without implied size");
+
+       if (op1size != op2size)
+               error("data sizes of two registers don't agree");
+       if (isize == UNKNOWN) return op1size;
+       else {
+               if (isize != op1size)
+                       error("data size of registers and inst don't agree");
+               return isize;
+       }
+}
+
+
+int yyerror(char *s)
+{
+       if (yytext[0] >= 32) {
+               fprintf(stderr, "%s near '%s', line %d\n",
+                       s, yytext, lineno);
+       } else {
+               fprintf(stderr, "%s, line %d\n", s, lineno - 1);
+       }
+       return 0;
+}
+
+void error(char *s)
+{
+       yyerror(s);
+}
+
+void fatal_error(char *s)
+{
+       yyerror(s);
+       exit(1);
+}
+
+int yywrap()
+{
+       return 1;
+}
diff --git a/as/xa51/xa_dasm.c b/as/xa51/xa_dasm.c
new file mode 100644 (file)
index 0000000..de055f0
--- /dev/null
@@ -0,0 +1,69 @@
+/* This file is part of Paul's XA51 Assembler, Copyright 1997,2002 Paul Stoffregen
+ *
+ * Paul's XA51 Assembler is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ *
+ * Foobar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Author contact: paul@pjrc.com */
+
+
+#include <stdio.h>
+
+#define MAXHEXLINE 32  /* the maximum number of bytes to put in one line */
+
+extern FILE *fhex;  /* the file to put intel hex into */
+
+
+/* produce intel hex file output */
+
+void hexout(int byte, int memory_location, int end)
+{
+       static int byte_buffer[MAXHEXLINE];
+       static int last_mem, buffer_pos, buffer_addr;
+       static int writing_in_progress=0;
+       register int i, sum;
+
+       if (!writing_in_progress) {
+               /* initial condition setup */
+               last_mem = memory_location-1;
+               buffer_pos = 0;
+               buffer_addr = memory_location;
+               writing_in_progress = 1;
+               }
+
+       if ( (memory_location != (last_mem+1)) || (buffer_pos >= MAXHEXLINE) \
+        || ((end) && (buffer_pos > 0)) ) {
+               /* it's time to dump the buffer to a line in the file */
+               fprintf(fhex, ":%02X%04X00", buffer_pos, buffer_addr);
+               sum = buffer_pos + ((buffer_addr>>8)&255) + (buffer_addr&255);
+               for (i=0; i < buffer_pos; i++) {
+                       fprintf(fhex, "%02X", byte_buffer[i]&255);
+                       sum += byte_buffer[i]&255;
+               }
+               fprintf(fhex, "%02X\n", (-sum)&255);
+               buffer_addr = memory_location;
+               buffer_pos = 0;
+       }
+
+       if (end) {
+               fprintf(fhex, ":00000001FF\n");  /* end of file marker */
+               fclose(fhex);
+               writing_in_progress = 0;
+       }
+               
+       last_mem = memory_location;
+       byte_buffer[buffer_pos] = byte & 255;
+       buffer_pos++;
+}
+
+
diff --git a/as/xa51/xa_main.c b/as/xa51/xa_main.c
new file mode 100644 (file)
index 0000000..da70628
--- /dev/null
@@ -0,0 +1,435 @@
+/* Paul's XA51 Assembler, Copyright 1997,2002 Paul Stoffregen (paul@pjrc.com)
+ *
+ * Paul's XA51 Assembler is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* adapted from the osu8asm project, 1995 */
+/* http://www.pjrc.com/tech/osu8/index.html */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xa_main.h"
+
+extern void yyrestart(FILE *new_file);
+extern void hexout(int byte, int memory_location, int end);
+extern int yyparse();
+
+
+/* global variables */
+
+FILE *fhex, *fmem, *list_fp;
+extern FILE *yyin;
+extern char *yytext;
+extern char last_line_text[];
+struct symbol *sym_list=NULL;
+struct target *targ_list=NULL;
+int lineno=1, mem=0;   /* mem is location in memory */
+int p1=0, p2=0, p3=0;
+int expr_result, expr_ok, jump_dest, inst;
+int opcode, operand;
+char symbol_name[1000];
+
+
+/* add symbols to list when we find their definition in pass #1 */
+/* we will evaluate their values in pass #2, and figure out if */
+/* they are branch targets betweem passes 1 and 2.  Every symbol */
+/* should appear exactly once in this list, since it can't be redefined */
+
+struct symbol * build_sym_list(char *thename)
+{
+       struct symbol *new, *p;
+
+/*     printf("  Symbol: %s  Line: %d\n", thename, lineno); */
+       new = (struct symbol *) malloc(sizeof(struct symbol));
+       new->name = (char *) malloc(strlen(thename)+1);
+       strcpy(new->name, thename);
+       new->value = 0;
+       new->istarget = 0;
+       new->isdef = 0;
+       new->isbit = 0;
+       new->isreg = 0;
+       new->line_def = lineno - 1;
+       new->next = NULL;
+       if (sym_list == NULL) return (sym_list = new);
+       p = sym_list;
+       while (p->next != NULL) p = p->next;
+       p->next = new;
+       return (new);
+}
+
+int assign_value(char *thename, int thevalue)
+{
+       struct symbol *p;
+
+       p = sym_list;
+       while (p != NULL) {
+               if (!(strcasecmp(thename, p->name))) {
+                       p->value = thevalue;
+                       p->isdef = 1;
+                       return (0);
+               }
+               p = p->next;
+       }
+       fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
+       exit(1);
+}
+
+int mk_bit(char *thename)
+{
+        struct symbol *p;
+
+        p = sym_list;
+        while (p != NULL) {
+                if (!(strcasecmp(thename, p->name))) {
+                        p->isbit = 1;
+                        return (0);
+                }
+                p = p->next;
+        }
+        fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
+        exit(1);
+}
+
+
+int mk_reg(char *thename)
+{
+        struct symbol *p;
+
+        p = sym_list;
+        while (p != NULL) {
+                if (!(strcasecmp(thename, p->name))) {
+                        p->isreg = 1;
+                        return (0);
+                }
+                p = p->next;
+        }
+        fprintf(stderr, "Internal Error!  Couldn't find symbol\n");
+        exit(1);
+}
+
+
+
+int get_value(char *thename)
+{
+       struct symbol *p;
+       p = sym_list;
+       while (p != NULL) {
+               if (!(strcasecmp(thename, p->name)))
+                       return (p->value);
+               p = p->next;
+       }
+       fprintf(stderr, "Internal Error!  Couldn't find symbol value\n");
+       exit(1);
+}
+               
+
+
+/* add every branch target to this list as we find them */
+/* ok if multiple entries of same symbol name in this list */
+
+struct target * build_target_list(char *thename)
+{
+       struct target *new, *p;
+       new = (struct target *) malloc(sizeof(struct target));
+       new->name = (char *) malloc(strlen(thename)+1);
+       strcpy(new->name, thename);
+       new->next = NULL;
+       if (targ_list == NULL) return (targ_list = new);
+       p = targ_list;
+       while (p->next != NULL) p = p->next;
+       p->next = new;
+       return (new);
+}
+
+/* figure out which symbols are branch targets */
+
+void flag_targets()
+{
+       struct symbol *p_sym;
+       struct target *p_targ;
+       p_targ = targ_list;
+       while (p_targ != NULL) {
+               p_sym = sym_list;
+               while (p_sym != NULL) {
+                       if (!strcasecmp(p_sym->name, p_targ->name))
+                               p_sym->istarget = 1;
+                       p_sym = p_sym->next;
+               }
+               p_targ = p_targ->next;
+       }
+}
+
+void print_symbol_table()
+{
+       struct symbol *p;
+       p = sym_list;
+       while (p != NULL) {
+               printf("Sym:%12s = %5d (%04X)  Def:", \
+                 p->name, p->value, p->value);
+               if (p->isdef) printf("Yes"); else printf("No ");
+               printf("  Bit:");
+               if (p->isbit) printf("Yes"); else printf("No ");
+               printf("  Target:");
+               if (p->istarget) printf("Yes"); else printf("No ");
+               printf(" Line %d\n", p->line_def);
+               p = p->next;
+       }
+}
+
+/* check that every symbol is in the table only once */
+
+void check_redefine()
+{
+       struct symbol *p1, *p2;
+       p1 = sym_list;
+       while (p1 != NULL) {
+               p2 = p1->next;
+               while (p2 != NULL) {
+                       if (!strcasecmp(p1->name, p2->name)) {
+                               fprintf(stderr, "Error: symbol '%s' redefined on line %d", p1->name, p2->line_def);
+                               fprintf(stderr, ", first defined on line %d\n", p1->line_def);
+                       exit(1);
+                       }
+                       p2 = p2->next;
+               }
+               p1 = p1->next;
+       }
+}
+
+int is_target(char *thename)
+{
+       struct symbol *p;
+       p = sym_list;
+       while (p != NULL) {
+               if (!strcasecmp(thename, p->name)) return (p->istarget);
+               p = p->next;
+       }
+       return (0);
+}
+
+int is_bit(char *thename)
+{
+        struct symbol *p;
+        p = sym_list;
+        while (p != NULL) {
+                if (!strcasecmp(thename, p->name)) return (p->isbit);
+                p = p->next;
+        }
+        return (0);
+}
+
+int is_reg(char *thename)
+{
+        struct symbol *p;
+        p = sym_list;
+        while (p != NULL) {
+                if (!strcasecmp(thename, p->name)) return (p->isreg);
+                p = p->next;
+        }
+        return (0);
+}
+
+
+int is_def(char *thename)
+{
+       struct symbol *p;
+       p = sym_list;
+       while (p != NULL) {
+               if (!strcasecmp(thename, p->name) && p->isdef) return(1);
+               p = p->next;
+       }
+       return (0);
+}
+
+/* this routine is used to dump a group of bytes to the output */
+/* it is responsible for generating the list file and sending */
+/* the bytes one at a time to the object code generator */
+/* this routine is also responsible for generatine the list file */
+/* though is it expected that the lexer has placed all the actual */
+/* original text from the line in "last_line_text" */
+
+void out(int *byte_list, int num)
+{
+       int i, first=1;
+
+       if (num > 0) fprintf(list_fp, "%06X: ", mem);
+       else fprintf(list_fp, "\t");
+
+       for (i=0; i<num; i++) {
+               hexout(byte_list[i], mem+i, 0);
+               if (!first && (i % 4) == 0) fprintf(list_fp, "\t");
+               fprintf(list_fp, "%02X", byte_list[i]);
+               if ((i+1) % 4 == 0) {
+                       if (first) fprintf(list_fp, "\t%s\n", last_line_text);
+                       else fprintf(list_fp, "\n");
+                       first = 0;
+               } else {
+                       if (i<num-1) fprintf(list_fp, " ");
+               }
+       }
+       if (first) {
+               if (num < 3) fprintf(list_fp, "\t");
+               fprintf(list_fp, "\t%s\n", last_line_text);
+       } else {
+               if (num % 4) fprintf(list_fp, "\n");
+       }
+}
+
+
+/* add NOPs to align memory location on a valid branch target address */
+
+void pad_with_nop()
+{
+       static int nops[] = {NOP_OPCODE, NOP_OPCODE, NOP_OPCODE, NOP_OPCODE};
+       int num;
+
+       last_line_text[0] = '\0';
+
+       for(num=0; (mem + num) % BRANCH_SPACING; num++) ;
+       if (p3) out(nops, num);
+       mem += num;
+}
+
+/* print branch out of bounds error */
+
+void boob_error()
+{
+       fprintf(stderr, "Error: branch out of bounds");
+       fprintf(stderr, " in line %d\n", lineno);
+       exit(1);
+}
+
+/* output the jump either direction on carry */
+/* jump_dest and mem must have the proper values */
+
+/* 
+void do_jump_on_carry()
+{
+       if (p3) {
+               operand = REL4(jump_dest, mem);
+               if (operand < 0) {
+                       operand *= -1;
+                       operand -= 1;
+                       if (operand > 15) boob_error();
+                       out(0x20 + (operand & 15));
+               } else {
+                       if (operand > 15) boob_error();
+                       out(0x30 + (operand & 15));
+               }
+       }
+}
+*/ 
+
+/* turn a string like "10010110b" into an int */
+
+int binary2int(char *str)
+{
+       register int i, j=1, sum=0;
+       
+       for (i=strlen(str)-2; i >= 0; i--) {
+               sum += j * (str[i] == '1');
+               j *= 2;
+       }
+       return (sum);
+}
+
+void print_usage();
+
+/* pass #1 (p1=1) find all symbol defs and branch target names */
+/* pass #2 (p2=1) align branch targets, evaluate all symbols */
+/* pass #3 (p3=1) produce object code */
+
+int main(int argc, char **argv)
+{
+       char infilename[200], outfilename[200], listfilename[200];
+
+       if (argc < 2) print_usage();
+       strcpy(infilename, argv[1]);
+       if(strlen(infilename) > 3) {
+               if (strncasecmp(infilename+strlen(infilename)-3, ".xa", 3))
+                       strcat(infilename, ".xa");
+       } else strcat(infilename, ".xa");
+       strcpy(outfilename, infilename);
+       outfilename[strlen(outfilename)-3] = '\0';
+       strcpy(listfilename, outfilename);
+       strcat(outfilename, ".hex");
+       strcat(listfilename, ".lst");
+       yyin = fopen(infilename, "r");
+       if (yyin == NULL) {
+               fprintf(stderr, "Can't open file '%s'.\n", infilename);
+               exit(1);
+       }
+       fhex = fopen(outfilename, "w");
+       if (fhex == NULL) {
+               fprintf(stderr, "Can't write file '%s'.\n", outfilename);
+               exit(1);
+       }
+       list_fp = fopen(listfilename, "w");
+       if (list_fp == NULL) {
+               fprintf(stderr, "Can't write file '%s'.\n", listfilename);
+               exit(1);
+       }
+
+       /* todo: add a command line option to supress verbose messages */
+       printf("\nPaul's XA51 Assembler\n");
+       printf("Copyright 1997,2002 Paul Stoffregen\n\n");
+       printf("This program is free software; you can redistribute it\n");
+       printf("and/or modify it under the terms of the GNU General Public\n");
+       printf("License, Version 2, published by the Free Software Foundation\n\n");
+       printf("This program is distributed in the hope that it will be useful,\n");
+       printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
+       printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+
+
+
+       printf("    Building Symbol Table:\n");
+       p1 = 1;
+       mem = 0;
+       yyparse();
+       flag_targets();
+       // print_symbol_table();
+       check_redefine();
+       p1 = 0;
+       p2 = 1;
+       rewind(yyin);
+       yyrestart(yyin);
+       lineno = 1;
+       printf("    Aligning Branch Targets:\n");
+       mem = 0;
+       yyparse();
+       // print_symbol_table();
+       p2 = 0;
+       p3 = 1;
+       rewind(yyin);
+       yyrestart(yyin);
+       lineno = 1;
+       printf("    Generating Object Code:\n");
+       mem = 0;
+       yyparse();
+       fclose(yyin);
+       hexout(0, 0, 1);  /* flush and close intel hex file output */
+       return 0;
+}
+
+
+void print_usage()
+{
+       fprintf(stderr, "Usage: xa_asm file\n");
+       fprintf(stderr, "   or  xa_asm file.asm\n");
+       exit(1);
+}
+
diff --git a/as/xa51/xa_main.h b/as/xa51/xa_main.h
new file mode 100644 (file)
index 0000000..6b1e30f
--- /dev/null
@@ -0,0 +1,61 @@
+#define SIZE8 0
+#define SIZE16 1
+#define SIZE32 2
+#define UNKNOWN -1
+#define WORD_REG 16384
+#define BYTE_REG 32768
+
+/* max # of bytes in db directive */
+#define MAX_DB          2500
+
+/* max # char in symbol name */
+#define MAX_SYMBOL      1024
+
+/* max # of bytes per line */
+#define MAX_LINE       4096
+
+/* REL() computes branch operand from dest and memory */
+/* location of the jump instruction itself */
+/* this is later adjusted by one for jcu, of course */
+#define BRANCH_SPACING 2
+#define REL(dest, mem) (((dest)-((((mem)+1)/(\
+BRANCH_SPACING))*(BRANCH_SPACING)))/(BRANCH_SPACING))
+
+#define NOP_OPCODE 0   /* opcode for NOP */
+
+/* a linked list of all the symbols */
+
+struct symbol {
+        char *name;
+        int value;
+        int istarget;   /* 1 if a branch target, 0 otherwise */
+        int isdef;      /* 1 if defined, 0 if no value yet */
+        int line_def;   /* line in which is was defined */
+        int isbit;      /* 1 if a bit address, 0 otherwise */
+       int isreg;      /* 1 if a register, 0 otehrwise */
+        struct symbol *next; };
+
+/* a list of all the symbols that are branch targets */
+/* (and will need to get aligned on 4 byte boundries) */
+
+struct target {
+        char *name;
+        struct target *next; };
+
+
+extern FILE *yyin;
+extern char *yytext;
+extern int lineno;
+extern int p1, p2, p3, mem, m_len;
+
+extern struct symbol * build_sym_list(char *thename);
+extern int assign_value(char *thename, int thevalue);
+extern int mk_bit(char *thename);
+extern int mk_reg(char *thename);
+extern void out(int *byte_list, int num);
+extern int is_target(char *thename);
+extern void pad_with_nop();
+extern int binary2int(char *str);
+extern int is_def(char *thename);
+extern int get_value(char *thename);
+