--- /dev/null
+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 *~
+
--- /dev/null
+
+;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]
+
+
--- /dev/null
+;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
+
--- /dev/null
+
+;$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
+
+
+
+
+
+
+
+
+
--- /dev/null
+; 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]
+
+
+
--- /dev/null
+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
+
--- /dev/null
+%{
+/* 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];}
+
+%%
--- /dev/null
+%{
+/* 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;
+}
--- /dev/null
+/* 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++;
+}
+
+
--- /dev/null
+/* 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);
+}
+
--- /dev/null
+#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);
+