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