.command -ai ; output in Intel hex format ;======================================================================== ; ; MOTOROLA 6809 "MICRO-TALKER" MONITOR PROGRAM ; (c) 1990, 1993 T-Recursive Technology ; placed into the public domain for free and unrestricted use ; ; A minimal monitor program for the Motorola 6809. ; Used in conjunction with a "smart" host program to examine & ; modify memory, and to set and execute breakpoints in machine ; language and Forth. ; ; The Talker program uses only registers for memory examine and ; modify, and only a small amount of stack RAM for breakpoints. ; Only about 300 bytes of PROM are used; no interrupts are used. ; The Talker may be operated half-duplex over a bidirectional ; serial line. ; ; The program accepts characters from a Signetics 2681 DUART. ; Characters 30h to 3Fh are treated as hex digits and are ; shifted into a one-byte data register. ; Characters 20h to 2Fh are command codes: ; 20-23 reserved for future use (ignored) ; group 2: breakpoint / debug support ; 24 = set a Forth "thread" breakpoint ; 25 = set a Forth "code field" breakpoint ; 26 = set a machine language breakpoint ; 27 = fetch lo byte of address (adrs lo -> data & output) ; 28 = fetch hi byte of address (adrs hi -> data & output) ; 29 = read back data register (data -> output) ; group 1: minimal talker ; 2A = fetch byte & incr addr (mem or reg -> data & output) ; 2B = store byte & incr addr (data -> mem or reg) ; 2C = set lo byte of address (data -> adrs lo) ; 2D = set hi byte of address (data -> adrs hi) ; 2E = set memory "page" (no function on 6809) ; 2F = "go" (jump to current adrs) ; ; Internal register usage: ; Y = address ; X (low byte) = data byte ; D (A,B) = working ; S = stack pointer ; ; Revision history ; v 1.0 23 Aug 89 original Super8 program ; v 1.1 25 Feb 90 support for breakpoints and Forth words ; v 1.2 7 May 90 function codes reassigned ; Z8 v 1.0 2 Dec 90 modified for Zilog Z8 ; 6809 v 1.0 25 Jul 93 modified for The Computer Journal 6809 ; Uniprocessor ; ;======================================================================== ; ; Macros for half-duplex communication on a bidirectional link, ; e.g., a bidirectional RS-485 serial line. Define these macros ; to control the transceiver connected to the serial port. ; If full-duplex is to be used (separate transmit and receive data ; lines), define these as "null" macros. ; ; N.B.: the PseudoSam Level I assembler does not support macros. ; 'TXON' and 'TXOFF' are commented out wherever used. ; ; TXON - turns serial port transmitter on, and receiver off. ; TXOFF - turns serial port transmitter off, and receiver on. ; ; txon .macro ; .endm ; ; txoff .macro ; .endm ;======================================================================== ; ; STANDALONE INITIALIZATION ; ; For use when the monitor is used as a standalone program ; in a 6809 development board. In this case, the monitor ; is located in high PROM, to be started on reset. The UART ; registers are "minimally" initialized, to allow full-duplex ; serial communication at 9600 baud, 8 bits, no parity. ; The interrupts are vectored to a supplementary jump table ; in RAM. ; ; Expects: reset state for all registers ; Returns: ; Uses: ; ;======================================================================== .org h'fed0 .equ spsave,0 ; RAM location for saving S .equ vecs,2 ; RAM address of a jump table .equ stack,h'100 ; RAM address of bottom of stack .equ duart,h'6000 ; base address of 2681 DUART .equ txemt,8 .equ txrdy,4 .equ rxrdy,1 ; 2681 Initialization Table. Each word in this table contains ; register-number:contents in the hi:lo bytes, respectively. ; initbl: .dw h'022a ; Command Register A: reset rx, disable rx & tx .dw h'0230 ; Command Register A: reset tx .dw h'0240 ; Command Register A: reset error status .dw h'0210 ; Command Register A: reset MR pointer .dw h'0013 ; Mode Register A(1): 8 bits, no parity .dw h'0007 ; Mode Register A(2): 1 stop, RTS & CTS manual .dw h'01bb ; Clock Select A: tx & rx 9600 baud .dw h'0205 ; Command Register A: enable rx & tx .dw h'0a2a ; Command Register B: reset rx, disable rx & tx .dw h'0a30 ; Command Register B: reset tx .dw h'0a40 ; Command Register B: reset error status .dw h'0a10 ; Command Register B: reset MR pointer .dw h'0813 ; Mode Register B(1): 8 bits, no parity .dw h'0807 ; Mode Register B(2): 1 stop, RTS & CTS manual .dw h'09bb ; Clock Select B: tx & rx 9600 baud .dw h'0a05 ; Command Register B: enable rx & tx .dw h'0430 ; Aux Control Register: counter mode, xtal/16 .dw h'062d ; Counter Upper, and .dw h'0700 ; Counter Lower: 2d00 hex = 50.000 msec .dw h'0d00 ; Output Port Configuration: all manual .dw h'0e03 ; Set Output Bits: OP0 and OP1 low .dw h'0500 ; Interrupt Mask Register: all disabled endtbl: ; The program enters here on a reset. ; The stack pointer is initialized to a location in RAM. This ; RAM is only needed if breakpoints are to be used -- the talker's ; memory examine & modify instructions don't use the stacks. main: lds #stack ; set stack pointer to top of page zero clra ; set direct page register to 0 tfr a,dpr ; Initialize DUART from the table above. ; ldy #initbl ldx #duart iloop: ldd ,y++ ; fetch a:b from table stb a,x ; store b at duart+a cmpy #endtbl bne iloop ; fall thru to talker ;======================================================================== ; ; TALKER MAIN ENTRY POINT ; MACHINE LANGUAGE BREAKPOINT ENTRY ; ; This is the main "talker" program. It performs the basic ; character processing routine "talk" repeatedly, until a monitor ; command transfers control to an application program. ; ; This is also the entry point for machine language breakpoints. ; A breakpoint consists of a software interrupt SWI3. This ; causes all registers to be pushed onto the stack. ; Should it be desirable to have an interrupt cause a breakpoint ; -- e.g., a "break" pushbutton wired to an interrupt input -- ; the same logic is used. ; ; The breakpoint routine copies the saved flag values into the ; talker's data register, and the saved return address into the ; talker's address register. An immediate "go" function will ; resume with these saved values (as well as the saved rp). ; ; Entering the breakpoint routine causes the '*' character to ; be sent to the host. Entering the monitor causes 'M' to be sent. ; Note also that the main entry point puts its own address onto ; the stack; this is so that we can "go" to a routine which ends ; in a RET. This "normal termination" can be distinguished from ; a breakpoint by the 'M' character. ; ; The stack frame built is: ; CCR A B DPR Xhi Xlo Yhi Ylo Uhi Ulo PChi PClo TALKERhi TALKERlo ; ^ ; | <-all of this is pushed by an interrupt,-> <-this is left-> ; S and is popped by a "go" command. when 'talker' ; is first entered. ; Expects: ; Returns: ; Uses: 14 bytes of stack ; ;======================================================================== talker: ldd #talker ; push the address 'talker' under the 'go' frame pshs d pshs pc,u,y,x,dp,b,a,cc ; push a dummy return frame lda #h'4d ; 'M' ..talker program entry point bra t0 ; Machine language breakpoints use SWI3, so they can use ; the same entry point as interrupt breakpoints. ; All registers are automatically saved in the return frame. mbreak: ; Machine language breakpoint entry ibreak: ; Interrupt breakpoint entry lda #h'2a ; '*' ; All entry points eventually land here t0: sts >spsave ; save stack pointer where host can find it t1: ldb duart+1 ; transmit character in A andb #txrdy beq t1 ; txon sta duart+3 t2: ldb duart+1 ; wait 'til character finished andb #txemt beq t2 ; txoff ldy 10,s ; get return address in address register ldb ,s ; get flags in data register clra tfr d,x ;======================================================================== ; ; TALK - SINGLE CHARACTER PROCESSING ROUTINE ; ; This routine processes one character received from the host. ; If no character is received, it loops. ; It may cause two characters to be transmitted to the host. ; ; Expects: ; Returns: ; Uses: D,X,Y,CCR ; ;======================================================================== done: ; talker is infinite loop talk: ldb duart+1 ; check for received character andb #rxrdy beq done ldb duart+3 ; get character clra ; most operations require A=0 andb #h'3f ; mask off all but low 6 bits subb #h'30 ; if less than 30 - bcs cmd ; - it's a command ; ; 30-3Fh: digit entry ; digit: andb #h'0f ; 30-3f: hex digit, shift into lo nybble exg d,x ; old MDR in D, for shifting aslb aslb aslb aslb ; D=00n0 X=000n abx ; add new to old, result in X bra done ; ; 24-2Fh: command codes ; We use a jump table to select the appropriate function routine. ; NOTE: Each routine is entered with Y=address, A=0, B=data. ; D and X must be swapped back before going back to 'done'! ; (The label 'cmdone' does this.) ; cmd: andb #h'0f ; convert command codes to 0..0F aslb ; *2 exg d,x ; table offset in X, data in D clra ; many commands require this jmp [table,x] ; function codes table: .dw cmdone ; 20 - no op .dw cmdone ; 21 - no op .dw cmdone ; 22 - no op .dw cmdone ; 23 - no op .dw settb ; 24 - set thread breakpoint .dw setcb ; 25 - set code field breakpoint .dw setmb ; 26 - set machine code breakpoint .dw getlo ; 27 - get low address .dw gethi ; 28 - get high address .dw echo ; 29 - get data register .dw fetch ; 2a - read memory & send to host .dw store ; 2b - store data at address .dw setlo ; 2c - set low address .dw sethi ; 2d - set high address .dw cmdone ; 2e - set extended address (no op) .dw go ; 2f - "call" address ; ; 24H: set a Forth "thread" breakpoint at the given address (2 bytes) ; this puts a the address of the "breakpoint" pseudo-word into ; a Forth thread (a list of addresses of Forth words). It is the ; responsibility of the user to ensure that this is a valid thread ; address, and to save the previous value. ; settb: ldx #tbreak stx ,y bra cmdone ; ; 25H: set a Forth "code field" breakpoint at the given address (2 bytes) ; this changes the code field associated with a given Forth word to ; point to a machine-language breakpoint routine. It is the ; responsibility of the user to ensure that this is a valid code ; field address, and to save the previous value. ; >>>For the 6809 DTC Forth, this is the same as function 26H<<< ; setcb: ; ; 26H: set a machine language breakpoint at the given address (2 bytes) ; this puts a machine-language SWI3 at the given address. It is ; the responsibility of the user to ensure that this is a valid ; instruction address, and to save the previous value. ; setmb: ldx #h'113f ; SWI3 instruction stx ,y bra cmdone ; ; 27H: copy low address byte to data register, and send to host ; Note that this destroys the previous data register contents. ; Use function 2Dh to save that value first, if needed. ; getlo: tfr y,d ; address to data register clra ; data register (hi) always zero! bra echo1 ; go send it ; ; 28H: copy high address byte to data register, and send to host ; Note that this destroys the previous data register contents. ; Use function 2Dh to save that value first, if needed. ; gethi: tfr y,d ; exg a,b ; address hi to data register (lo) clra ; data reg (hi) always zero bra echo1 ; go send it ; ; 29H: read back contents of data register ; echo: echo1: ; txon ; turn on transmitter (tx buf is empty) tfr b,a ; transmit high nybble lsra lsra lsra lsra ora #h'30 sta duart+3 tx1: lda duart+1 ; wait 'til buffer ready for 2nd char anda #txrdy beq tx1 tfr b,a ; transmit low nybble anda #h'0f ora #h'30 sta duart+3 tx2: lda duart+1 ; wait 'til character finished anda #txemt beq tx2 ; txoff cmdone: clra ; needed after 'echo', otherwise just in case exg d,x lbra done ; ; 2AH: fetch byte from memory and transmit ; fetch: ldb ,y+ ; fetch and increment address bra echo1 ; go send data ; ; 2BH: store byte in memory ; store: stb ,y+ bra cmdone ; ; 2CH: set low address byte ; D=00nn Y=hhll --> Y=hhnn D=00nn ; setlo: exg y,d clrb exg y,d ; Y=hh00 leay d,y ; Y=hhnn bra cmdone ; ; 2DH: set high address byte ; D=00nn Y=hhll --> Y=nnll ; sethi: exg y,d clra exg y,d ; Y=00ll exg a,b leay d,y ; Y=nnll exg a,b ; D=00nn bra cmdone ; ; 2EH: set extended address byte (memory page) ; >>> not used on the 6809 <<< ; ; ; 2FH: go to given address (resume execution at given address) ; ; Note: if breakpoints are not being used, ; go: jmp ,y ; is an acceptable substitute here. ; go: sty 10,s ; store program counter in return frame stb ,s ; store flags in return frame orcc #h'80 ; force all regs popped on 'rti' rti ; restore regs & go to given address ;======================================================================== ; ; FORTH LANGUAGE BREAKPOINT ENTRIES ; ; These are the entry points for Forth language breakpoints. ; ; The first is the "code field" breakpoint. This is an address ; which can be stored in a Forth word's code field, to cause a ; break whenever that word is executed. This kind of breakpoint ; can be set in any Forth word. ; >>> In the 6809 Direct-Threaded-Code implementation, the ; parameter field of every Forth word begins with machine code. ; So, an ordinary machine-code breakpoint can be set in the ; first byte of a word. <<< ; ; The second kind is the "thread" breakpoint. This is an address ; which can be patched into a high-level "thread", to cause a ; break when a certain point is reached in high-level code. The ; thread is a series of addresses of Forth words, executed by the ; inner or "NEXT" interpreter. So, we provide the address of a ; dummy Forth word whose execution action is to invoke a machine ; language breakpoint. The Forth execution state can be deduced ; from the registers. ; ;======================================================================== .equ cbreak,mbreak ; the code field breakpoint is simply ; a machine language breakpoint set ; in a DTC "code field" ; what follows is the parameter and code field of a "headerless" ; Forth word, to invoke the breakpoint routine. (In DTC, this is ; simply a machine code fragment which does a breakpoint.) tbreak: swi3 ; the thread breakpoint is simply ; a pointer to this code fragment ;======================================================================== ; ; 6809 RESET AND INTERRUPT VECTORS ; ;======================================================================== ; Reset and SWI3 are used by the talker program. ; All other interrupts are vectored to a jump table in low RAM. .org h'fff2 .dw mbreak ; SWI3 - breakpoint .dw vecs+0 ; SWI2 .dw vecs+4 ; FIRQ .dw vecs+8 ; IRQ .dw vecs+h'0c ; SWI .dw vecs+h'10 ; NMI .dw main ; RESET - init & enter talker .end