#define PCODE_INTERPRETER //* #define DEBUG //* #define BETA #include "pcode.h" //* //* //* PCODE INTERPRETER for C and Logo compilers //* //* by Randy Sargent and Fred Martin //* Some changes by Anne Wright //* PWM update by Julian Skidmore //* //* //* REVISION HISTORY: //* //* VERSION 1.0 //* first created March 16, 1990 //* //* used in the Computer Museum contest with Logo compiler in April, 1990 //* //* update July 25, 1990: //* reconstructed "execute on startup" code, FGM //* //* //* changes begun Oct 7, 1990: //* added support for arrays and floating point. //* //* changes as of Dec 23: begin support for multitasking. //* //* First release V1.00 //* LCD printing Fri Jan 11 00:45:53 1991 //* PWM motor speed Fri Jan 11 00:46:00 1991 //* LCD translation table Fri Jan 11 01:26:58 1991 //* //* V1.01 Sun Jan 13 23:58:31 1991 //* fixed printf \n bug; 2nd \n was printed directly, now ignored. //* //* V1.10 Thu Jan 17 21:39:56 1991 //* swapped order of arguments to Pstartprocess to ticks first. //* installed version number at end of RAM. //* //* V1.20 Thu Jan 17 21:39:56 1991 //* added zero length array next to the end of RAM. //* fixed arefs to work //* added checkstack, jump from stack pcodes //* added pushblock pcode //* //* V1.30 Tue Jan 22 00:25:40 1991 //* made floating point trig routines use radians //* added Psetsp //* //* V1.31 Tue Jan 22 14:10:17 1991 //* enabled powerdown and TOC5 interrupts from within 1kHz routine //* //* V1.32 Tue Jan 22 23:48:54 1991 //* fixed bug in mret_die whereby scheduler tried to use dead process //* //* V1.40 Wed Jan 23 01:37:58 1991 //* more convincingly fixed previous bug; //* fixed PSTAT_DEAD to return properly and not signal UI //* //* V1.50 //* V1.60 //* lost in power outage, Fri Jan 25 1991 //* //* V1.70 Sun Jan 27 22:39:27 1991 //* arc tangent fixed //* floating point add and sub modified so they cannot return error //* installed trap for error 0 (which shouldn't ever happen) //* Pbitset, Pbitclr pcode instructions added //* IR decoding installed //* //* V1.80 Mon Jan 28 00:55:07 1991 //* Pfneg added //* //* V1.90 Mon Jan 28 01:13:23 1991 //* memory map reorganized to optimize code space //* //* V1.91 Tue Jan 29 02:37:11 1991 //* quadrature encoder code added //* //* V1.92 Sat Feb 2 00:08:32 1991 //* manual power-down + boot-up memory test added //* //* V2.00 //* added benchmark pcode //* allow control of SystemInt fcns //* allow control of IR transmission //* randy's PWM routine //* //* V2.01 //* minor optimizations to IR detection. //* //* V2.10 //* moved memory to be compatible with special vs. non-special mode //* //* V2.11 //* put vectors both at bfc0 and ffc0 so same pcode may be used on different //* boards //* //* V2.20 //* new opcodes (unimplemented) for support of longs, defer //* //* V2.21 //* merged rev2 and bookbot sources (#define SIMPLE for bookbot) //* //* V2.30 Sun Apr 7 19:55:19 1991 //* implemented 32-bit integer operations Padd4, Psub4, Plt4, Pgt4, and Pequal4; //* Pdefer, and Psystime //* //* V2.40 Sun Apr 7 22:10:03 1991 //* implemented Pflt2lng, Plng2flt, and Pmult4 //* converted several JSR's to BSR's due to new assembler suggestions //* //* V2.41 Thu May 23 14:25:41 1991 //* brought over some yamabico, bookbot changes from ai lab //* make BOOKBOT define for bookbot //* //* V2.42 Mon Jul 15 09:38:42 1991 fgm //* wrote PWM code for BOOKBOT //* //* V2.50 Tue Sep 17 21:36:24 1991 jrs //* wrote Pcallml //* //* V2.51 Sun Sep 22 19:05:32 1991 fgm //* made some changes for s-bot: frob switch addressing, default motor //* state, LCD display control, power-off based on battery level sensing. //* //* V2.52 Wed Sep 25 21:01:58 1991 fgm //* turned off TIC1 (power-off interrupt) for SBOT; set up port D mux //* pins for output. //* //* V2.60 Thu Oct 10 17:58:48 1991 fgm //* wrote Pinitint; installed #defines for LM576 and LM16117A LCD //* drivers. //* //* V2.61 Mon Oct 14 12:50:36 1991 fgm //* made SBOT powerdown routine halt motors //* //* V2.62 Thu Oct 17 09:51:04 1991 rs //* made battery voltage parameterizable in compile //* //* V2.63 Thu Oct 31 16:47:56 1991 fgm //* made sure that speaker is turned off after boot beep //* //* V2.64 Mon Jan 6 17:44:06 1992 fgm //* installed REV21 flag //* added new LCD flags: LCD_ROWS and LCD_COLS //* old LCD flag LM576 needed in addition to declare weird Rev 2 LCD //* //* V2.65 Sun Jan 12 13:30:29 1992 fgm //* enabled OC3 interrupt within System Interrupt //* to allow servo ctrl routine to operate properly //* //* V2.66 Wed Jan 15 13:40:57 1992 fgm, rs //* fixed nasty bug in FLT2LNG routine in "math11routines.asm" //* //* V2.67 Thu Feb 6 19:24:50 1992 rs //* made zero page variable to allow IC to ignore incoming serial //* //* V2.70 Mon Jan 4 20:09:20 EST 1993 anarch //* added aref_arb for arbitrary size elements in an array //* //* V2.71 Mon Jan 4 20:09:20 EST 1993 //* added jumping jack heartbeat rs //* Changed frobby character to respond to process loading anarch //* //* V2.8 //* Experimental version for implementing more efficient globals anarch //* Changes later taken out //* //* V2.81 //* Cleaned up for use with IC version 2.8, new distribution //* //* V2.81HB Sun Jun 11 08:39:00 1995 fredm //* Added #defines to support the Handy Board //* fixed bug with use of "FDB" rather than "RMB" in zero page //* //* V2.82HB Fri Oct 24 22:39:00 GMT 1997 skidmorj@cs.man.ac.uk //* Modified Handyboard code to support Smooth-PWM algorithm. //* //* 6811 equates EEPROM EQU $F800 ; start of eeprom #include "6811regs.asm" //* Masks for serial port PORTD_WOM EQU $20 BAUD1200 EQU $B3 BAUD9600 EQU $B0 TRENA EQU $0C ; Transmit, Receive ENAble RDRF EQU $20 ; Receive Data Register Full TDRE EQU $80 ; Transmit Data Register Empty //* definitions for V2.0 board #if defined (SBOT) | defined (REV21) | defined (HANDYBOARD) MOTORS_OFF EQU $0A #else MOTORS_OFF EQU $FF #endif #if defined (HANDYBOARD) DIGOUTPUT EQU $7F00 #else DIGOUTPUT EQU $7000 #endif #ifndef SBOT #if defined (HANDYBOARD) DIGINPUT EQU $7F00 #else DIGINPUT EQU $7000 #endif #else DIGINPUT EQU $5000 #endif FULL_POWER EQU 255 EXPANSION_INPUTS EQU $4000 //* ASCII definitions CR EQU $0a //***************************************************************** //* zero page RAM definitions ORG $00 srhi RMB 1 ; 0 srlo RMB 1 ; 1 sr2hi RMB 1 ; 2 sr2lo RMB 1 ; 3 sr3hi RMB 1 ; 4 sr3lo RMB 1 ; 5 sr4hi RMB 1 ; 6 sr4lo: signbit RMB 1 ; 7 don't use sr4lo and signbit simultaneously' current_process RMB 2 ; 8 addr. of proc entry in scheduler table process_ticks RMB 1 ; 0a system_status RMB 1 ; 0b process_counter RMB 2 ; 0c motor RMB 1 ; 0e output byte to PWM routine lcd_status RMB 1 ; 0f lcd_savedchar RMB 1 ; 10 lcd_char_count RMB 1 ; 11 number of chars printed since newline system_time_hi RMB 2 ; 12 system_time_lo RMB 2 ; 14 print_buffer_end RMB 2 ; 16 serial_buffer_pos RMB 2 ; 18 lcd_buffer_pos RMB 2 ; 1a lcd_frobline1 RMB 1 ; 1c XX XX XX I2 I1 I3 I4 I5 lcd_frobline2 RMB 1 ; 1d XX XX XX XX XX XX I7 I6 scheduler_iterations RMB 2 ; 1e count of # times through scheduler loop curmotor RMB 1 ; 20 used by PWM routine motorout RMB 1 ; 21 ditto speeda RMB 1 ; 22 motor A speed speedb RMB 1 speedc RMB 1 speedd RMB 1 beeptone RMB 2 IRtone RMB 2 IRdetect0 RMB 1 IRdetect1 RMB 1 IRdetect2 RMB 1 IRphase0 RMB 1 IRphase1 RMB 1 IRphase2 RMB 1 IRerror0 RMB 1 IRerror1 RMB 1 IRerror2 RMB 1 scounter0 RMB 2 ; shaft encoder 0 sstate0 RMB 1 scounter1 RMB 2 ; shaft encoder 1 sstate1 RMB 1 system_functions RMB 1 ; determines code run by SystemInt pcoderegister0 RMB 2 ignoreserial RMB 1 #if defined (SBOT) | defined (REV21) | defined (HANDYBOARD) | defined(SMOOTHPWM) gSpeedFracA RMB 1 gSpeedFracB RMB 1 gSpeedFracC RMB 1 gSpeedFracD RMB 1 ;Note it isn't a problem if these are overwritten occasionally!' #endif #include "math11vars.asm" LCDstart EQU * //***************************************************************** //* static RAM definitions (battery-backed) ORG PCODE_ORIGIN FCB 0 ; don't do autostart after a download' ORG JUMPTABLE //*** START OF JUMPTABLE jumptable: #include "pcode_inM.h" //*** END OF JUMPTABLE ORG MAIN_CODE startup: //* see if THE_ZERO_ARRAY is zero. If so, skip memory check. LDD THE_ZERO_ARRAY BEQ startupokay //* perform memory check with SP at $FF. If doesn't match checksum //* word, print error message and die. LDS #$FF JSR memorysum CMPD THE_ZERO_ARRAY BNE memory_bad LDD #0 STD THE_ZERO_ARRAY ; store 0 there meaning OK BRA startupokay memory_bad JSR LCDinit LDX #memorybadmsg JSR printstringLCD //* wait for button to continue button_wait LDAA DIGINPUT #if defined (SBOT) | defined (REV21) ANDA #$03 EORA #$03 #endif #if defined (HANDYBOARD) ANDA #$C0 ; handyboard buttons are top two bits EORA #$C0 #endif BEQ button_wait BRA normalinit memorybadmsg FCC 'MEMORY FAULT' FCB 0 startupokay LDS #MACHINE_STACK ; Initialize Stack Pointer to top of RAM CLR ignoreserial ; allow serial communication #ifdef DEBUG //* check for frob 1 or 2 being pressed. If so, do minimal init, //* and jump to a loop that just serves UI. LDAA DIGINPUT #if defined (SBOT) | defined (REV21) ANDA #$03 EORA #$03 #endif #if defined (HANDYBOARD) ANDA #$C0 ; handyboard buttons are top two bits EORA #$C0 #endif BEQ normalinit BRA init_sequence ; expect it to jump out! #endif normalinit BSR init_sequence ; turns off motors, inits serial to 9600 baud, //* inits pcode stack ptr (X reg) //* check for buttons 1 AND 0 being pressed. If so, //* do checksum, store, and die. #if defined (HANDYBOARD) BRA regularinit ;can't press both buttons on HB' //* because then it goes into bootstrap #endif LDAA DIGINPUT ANDA #$03 #if defined (SBOT) | defined (REV21) EORA #$03 #endif CMPA #3 BNE regularinit //* move stack to top of ZP ram so that we don't change memory LDS #$FF SEI ; disable interrupts JSR memorysum ; perform checksum of memory STD THE_ZERO_ARRAY ; store checksum JMP boarddies regularinit //* if button 1 or 0 pressed, don't do execute on startup. LDAA DIGINPUT #if defined (SBOT) | defined (REV21) ANDA #$03 EORA #$03 #endif #if defined (HANDYBOARD) ANDA #$80 ; HB "Start" button EORA #$80 #endif BEQ check_for_autostart jmp_scheduler JMP scheduler check_for_autostart //* check for "execute on startup" LDAA PCODE_ORIGIN ; start of pcode program DECA ; if was one, do autostart BNE jmp_scheduler autostart //* use UI's area to execute code starting at PCODE_ORIGIN+1 LDD #PCODE_ORIGIN+1 LDX #PROCESS_TABLE STD P_PC,X LDAA #PSTAT_RUNNING STAA P_STATUS,X JMP scheduler //************************************************************************* init_sequence //* clear IR detect, phase, and error bytes //* clear shaft counter and state bytes LDX #IRdetect0 CLRA IR_initloop STAA 0,X INX CPX #sstate1+1 BNE IR_initloop LDX #$1000 #ifndef BOOKBOT //* turn off motors (assuming "1" is off) LDAA #MOTORS_OFF STAA DIGOUTPUT STAA motor //* set motor powers to maximum LDAA #FULL_POWER STAA speeda STAA speedb STAA speedc STAA speedd #endif #ifdef BOOKBOT //* initialize ports a and d correctly BSET PACTL,X %00001000 LDAA #%00111110 STAA DDRD,X //* clear speeda and speedb so that motors are off CLR speeda CLR speedb #endif //* set beeper tone LDD #$7C0 STD beeptone //* turn on analog system BSET OPTION,X $80 #if defined (SBOT) | defined (REV21) //* initialize port D for output to control analog muxes BSET DDRD,X %00111000 #endif #if defined (REV2) | defined (YAMABICO) //* set IR tone based on switch 6 (bit 4) of expansion board LDAA #%10000000 STAA PACTL,X ; make TOC1 output BSET PORTA,X $80 ; bit on == IR off LDAA EXPANSION_INPUTS ANDA #$10 BEQ init_irtone0 LDD #8000 ; 125 Hz. STD IRtone BRA init_serial init_irtone0 LDD #10000 ; 100 Hz. STD IRtone #endif init_serial //* initialize serial port BCLR SPCR,X PORTD_WOM ; turn off wired-or mode LDAA #BAUD9600 STAA BAUD,X LDAA #TRENA STAA SCCR2,X //* tell SystemInt what to do #ifndef BOOKBOT LDAA #PRINTBUFFER+IRDECODE+PWM ; shaft encoding is defaulted off #else LDAA #PRINTBUFFER+PWM ; shaft encoding + IR defaulted off #endif STAA system_functions //* copy LCD driver to ZP ram and clear display JSR LCDinit //******************************** //* SYSTEM INITIALIZATION //* initialize process counter LDD #0 STD process_counter //* initialize system time STD system_time_hi STD system_time_lo //* initialize system status LDAA #SYSSTAT_IGNOREUI STAA system_status //* initialize output buffer LDD #PRINT_BUFFER STD print_buffer_end STD serial_buffer_pos STD lcd_buffer_pos //* initialize pcode loop LDAA #$20 ; BRA opcode STAA pcode_branch //******************************** //******************************** //* PROCESS INITIALIZATION //* //* initialize process table by setting all statuses to "PSTAT_DEAD" LDX #PROCESS_TABLE LDAB #MAX_PROCESSES LDAA #PSTAT_DEAD //* procinitloop STAA P_STATUS,X XGDX ADDD #PROCESS_SLOT_LENGTH XGDX DECB BNE procinitloop //* #ifdef DEBUG LDAA #$55 STAA motor STAA DIGOUTPUT #endif //* initialize UI process and install as current process LDX #PROCESS_TABLE STX current_process ; system current process LDD #UI_PROCESS_BUFFER STD P_PC,X LDD #PCODE_STACK_BEGIN STD P_STACK_ORG,X ADDD #1 ; must decrement before push STD P_SP,X SUBD #UI_STACK_SIZE STD P_STACK_LIM,X LDAA #UI_TICKS STAA P_TICKS,X LDAA #PSTAT_HALTED STAA P_STATUS,X STX P_PREV,X STX P_NEXT,X LDD process_counter ADDD #1 STD process_counter STD P_ID,X ; UI is PID #1 //* //******************************** initcontinuespecial //* initialize interrupt vectors and turn interrupts on JSR Pinitint //* print hello message on LCD screen LDX #versionmessage initmsgloop LDAA 0,X BEQ initcontinue JSR putchar_always ; assume buffer is empty INX BRA initmsgloop initcontinue #ifndef NO_BEEPER //* make a beep LDX #$1000 BSET TCTL1,X $01 LDY #$3000 DEY BNE *-2 BCLR TCTL1,X $01 BCLR PORTA,X $08 ; turn off speaker when done //* poke 0 as tone so as to reduce # of interrupts LDD #0 STD beeptone #endif RTS versionmessage #if LCD_ROWS * LCD_COLS <= 16 FCC 'ICv2.81 9/28/93' FCB $0a ; carriage return FCB 0 #else FCC 'Interactive C V 2.82 6/11/97' FCB $0a ; carriage return FCB 0 #endif //************************************************************************* //* //* UI_COMMAND //* a digit is {@ABC DEFG HIJK LMNO} //* x means ignored //* read //* r addr: x x x x dig dig dig dig-> dig dig //* write //* w addr: x x data-dig data-dig dig dig dig dig -> 'w' //* clear/set bits //* c addr: AND'ing-byte OR'ing-byte ADDR-hi ADDR-lo -> 'c' //* reset //* s : x x x x x x x x -> '>' //* blockwrite //* 'b' x byte-cnt-lo ADDR-hi ADDR-lo -> 'b' //* data-1 ... data-n -> chksum-byte of all bytes //* //* //* //* if read get address: print @address: goto command_loop //* if write get address,data: poke address,data: goto command_loop //* if reset goto reset //* //* ui_command: //* get a command character //* a command is always 'a' or bigger //* and data is always smaller than 'a' (@-O) command_get_type BSR getchar BSR serial_putchar ; echo back command type command_proc_command CMPA #'a;' BLO command_get_type //* check for reset command CMPA #'s;' BNE command_getargs LDAA #'>;' BRA serial_putchar ; send prompt and finish command_getargs PSHA ; save command type //* get 2 words of data BSR getword ; data (lsb), new pc for call: y PSHX PULY BSR getword ; address: x //* branch on command type PULA ; restore command type CMPA #'r;' BEQ command_read CMPA #'w;' BEQ command_write CMPA #'c;' BEQ command_clear CMPA #'b;' BEQ command_blockwrite RTS //* command_read LDAA 0,X BSR putbyte BRA command_promptreturn command_write XGDY ; move y to d. low byte to poke is now in b STAB 0,X BRA command_promptreturn command_clear XGDY ; move y to d. ANDA 0,X STAA 0,X ORAB 0,X STAB 0,X //* fall through to command_promptreturn command_promptreturn LDAA #'>;' BRA serial_putchar command_blockwrite //* uses srlo to compute checksum XGDY ; A = trash, B = bytecount //* compute checksum of X-hi, X-low, and B PSHB PSHX PULA PULB ABA PULB ; B restored = bytecount ABA ; A = checksum STAA srlo ; save checksum TSTB BEQ cmd_bwloopend cmd_bwloop BSR getbyte ; data STAA 0,X ADDA srlo STAA srlo ; checksum INX DECB BNE cmd_bwloop cmd_bwloopend LDAA srlo BSR putbyte BRA command_promptreturn //* get a word into X getword: BSR getbyte ; get hi byte first PSHA BSR getbyte TAB PULA XGDX RTS //* get a character into A getbyte: getchar: LDAA SCSR ANDA #RDRF BEQ getchar LDAA SCDR RTS //* put a character from A. munges B putbyte: serial_putchar: LDAB SCSR ANDB #TDRE BEQ serial_putchar STAA SCDR RTS //**************************************************************** //* //* pcode_run_process //* //* call with "current_process" pointer already loaded. //* //* executes pcode until //* system interrupt decrement "process_ticks" to zero, //* which then pokes "BRN" [branch never] into pcode_run loop. //* OR //* somebody else (i.e., an error-causing pcode) //* pokes the BRN. //* //* then saves process stack and PC and returns. //* //* returns with process status in A register. //* //* //* pcode_run_process: LDX current_process LDAA P_TICKS,X STAA process_ticks LDY P_PC,X LDX P_SP,X pcode_run LDAB 0,Y ; [5] load opcode STAB pcr_loadjump+2 ; [4] low byte of load INY ; [4] skip past opcode pcr_loadjump LDD JUMPTABLE ; [3] D gets loaded from jump table STD pcr_pcjsr+1 ; [5] pcr_pcjsr JSR $FFFF ; [6] loc'n gets stuffed;' pcode_branch: BRA pcode_run ; [3] //* poke me please //* finish up and exit LDAA #$20 ; BRA opcode STAA pcode_branch ; restore loop LDD current_process XGDX ; X has ptr to proc slot, D has proc SP STD P_SP,X ; store SP STY P_PC,X ; store process PC LDAA P_STATUS,X ; get status into A CMPA #PSTAT_DEAD BNE pcr_done //* process has died. back up to previous process and store as //* current_process for scheduler. LDX P_PREV,X STX current_process pcr_done RTS ; return to scheduler //********************************************************************** //* //* SCHEDULER //* //* Scheduler gives each process its turn in order. //* //* When P_STATUS of a process returns anything but STATUS_RUNNING, //* that status is bitwise ORed into the system status and the system //* status is broadcast to UI. //* //* In between process calls to "pcode_run_process~, the scheduler //* checks to see if the system status has changed (e.g., stdout //* is waiting), or if a serial interrupt has occured (e.g., UI //* wants attention.) //* //* If a process returns an error code, then all execution is halted, //* and the scheduler waits for the system status to change (UI must //* poke a new value into the status byte). It also responds to //* serial interrupts (UI attention). //* //* scheduler: //* check serial line status //* if we are supposed to ignore serial, then ignore it LDAA ignoreserial BNE sch_check_sysstat LDX #$1000 BRCLR SCSR,X RDRF sch_check_sysstat ; if clear, no byte ready JSR ui_command ; serve UI BRA scheduler sch_check_sysstat //* check system status BRSET system_status SYSSTAT_ERRORHALT scheduler //* ; if error, wait for UI to fix it sch_get_process //* get next process //* count if hit top of process table LDX current_process LDX P_NEXT,X STX current_process ; save new proc ptr CPX #PROCESS_TABLE BNE sch_frobbing_done LDD scheduler_iterations ADDD #1 STD scheduler_iterations sch_frobbing_done //* check process status LDAA P_STATUS,X CMPA #PSTAT_RUNNING BNE scheduler ; if this one isn't running, try;' //* again JSR pcode_run_process //* check for any error codes CMPA #PSTAT_RUNNING BEQ scheduler ; ok CMPA #PSTAT_DEAD BEQ scheduler CMPA #PSTAT_HALTED ; pcode-requested halt errorcode BEQ sch_halterr LDAA #SYSSTAT_ERRORHALT BRA sch_errornotify sch_halterr LDAA #SYSSTAT_HALTNOTIFY sch_errornotify BSR set_system_status BRA scheduler //********************************************************************** //* //* set_system_status //* //* takes byte in A as new system status //* if different from old, broadcasts new status byte. //* set_system_status: STAA srlo ANDA system_status CMPA srlo ; did any bits change? BNE change_sysstat RTS change_sysstat LDAA srlo ORAA system_status ; OR with old status STAA system_status JMP serial_putchar ; send status byte & return //********************************************************************* //* //* GUIDELINES FOR BEING A PCODE HANDLER //* //* When pcode instructions begin, Y register points at immediate //* arg if there is one. //* //* You must increment the Y register past any immediate args before //* exiting. //* //* 0,X is contents of top of stack; //* 2,X is contents of 2nd to top (assuming 16-bit data). //* //* Stack is growing downwards, so incrementing X will pop an item //* off of the stack. //* //* Storing an item at 2,X and then incrementing X twice is a good //* way for a binary operation to finish (16-bit). //* //***************************************************************** //*** *** //*** 16-BIT OPERATIONS *** //*** *** //***************************************************************** //*********************** //*** BINARY OPERATIONS //*********************** //***************************************************************** //* //* subtract2 //* Psub2 LDD 2,X SUBD 0,X BRA hpoppushd //***************************************************************** //* //* and2 //* Pbitand2 LDD 2,X ANDA 0,X ANDB 1,X BRA hpoppushd //***************************************************************** //* //* or2 //* Pbitor2 LDD 2,X ORAA 0,X ORAB 1,X BRA hpoppushd //***************************************************************** //* //* xor2 //* Pbitxor2 LDD 2,X EORA 0,X EORB 1,X BRA hpoppushd //***************************************************************** //* //* addition: pop two items off of stack and add them //* pop: pop one item off of the stack //* Padd2 LDD 0,X ADDD 2,X hpoppushd: STD 2,X Ppop2 INX INX ; pop useless item RTS //***************************************************************** //* //* multiply2 //* Pmult2: BSR top2toabs LDAA 1,X ; arg 2 lo LDAB 3,X ; arg 1 lo MUL STD srhi LDAA 0,X ; arg 2 hi LDAB 3,X ; arg 1 lo MUL ADDB srhi STAB srhi LDD 1,X ; don't look;' MUL ADDB srhi STAB srhi absret //* check for negation before exiting TST signbit BEQ hmultplus CLRA CLRB SUBD srhi BRA hpoppushd hmultplus LDD srhi BRA hpoppushd //* makes top two args on stack into absolute values, toggling sign bit top2toabs CLR signbit BSR Pabs2 ; makes absolute TOS, toggle signbit if - INX INX BSR Pabs2 DEX DEX RTS //***************************************************************** //* //* divide2 //* Pdiv2 JSR top2toabs LDD 2,X ; dividend PSHX LDX 0,X ; divisor IDIV STX srhi PULX BRA absret //***************************************************************** //* //* equal2 //* Pequal2 LDD 0,X SUBD 2,X BEQ pushtrue pushfalse LDD #0 BRA hpoppushd pushtrue LDD #1 BRA hpoppushd //***************************************************************** //* //* greaterthan2 //* Pgt2 LDD 2,X SUBD 0,X BGT pushtrue BRA pushfalse //***************************************************************** //* //* lessthan2 2,x < 0,x? //* Plt2 LDD 2,X SUBD 0,X BLT pushtrue BRA pushfalse //***************************************************************** //* //* lshift //* //* shifts next to top-of-stack left by top-of-stack bits. //* shifts t the right if t.o.s. is negative. //* //* uses self-modifying code Plshift PSHX LDD 0,X ; get count BMI rshift ; if minus shift right BNE golshift PULX INX INX RTS ; pop count and return golshift PSHB PSHA ; push counts on stack LDAA #$05 ; ASLD opcode STAA lshiftloop goshift LDD 2,X ; load # to be shifted PULX ; counts are in X lshiftloop FCB 0 ; shift opcode goes here BEQ shiftdone DEX BNE lshiftloop //* answer is in D; pull X to get SP back shiftdone PULX INX INX STD 0,X RTS rshift CLRA CLRB SUBD 0,X ; complement D PSHB PSHA LDAA #$04 ; LSRD opcode STAA lshiftloop BRA goshift //***************************************************************** //* //* absolute value2 //* used by other pcodes (don't punt me) Pabs2 TST 0,X BPL habsdone CLRA CLRB SUBD 0,X STD 0,X COM signbit ; record that we changed the sign habsdone RTS //*********************** //*** UNARY OPERATIONS //*********************** //***************************************************************** //* //* bitnot2: bitwise not //* Pbitnot2 COM 0,X COM 1,X RTS //***************************************************************** //* //* neg2: two's complement 0 - [0,X] => [0,X] //* Pneg2 CLRA CLRB SUBD 0,X STD 0,X RTS //***************************************************************** //* //* logidn: logical identity //* //* 0 ==> 0 //* else ==> 1 //* Plogidn2 LDD 0,X BEQ ret storetrue LDD #1 STD 0,X ret RTS //***************************************************************** //* //* lognot2: logical not //* //* 0 ==> 1 //* else ==> 0 //* Plognot2 LDD 0,X BEQ storetrue storefalse CLRA CLRB STD 0,X RTS //***************************************************************** //*** *** //*** 32-BIT OPERATIONS *** //*** *** //***************************************************************** //*********************** //*** BINARY OPERATIONS //*********************** //***************************************************************** //* //* longword addition //* Padd4: LDD 2,X ; low word, 2nd arg ADDD 6,X ; low word, 1st arg STD srhi LDAB 1,X ; 3rd byte, 2nd arg ADCB 5,X ; 3rd byte, 1st arg LDAA 0,X ADCA 4,X ; D has hi word Pbinlongrtn: STD 4,X LDD srhi STD 6,X LDAB #4 ABX RTS //***************************************************************** //* //* longword subtraction //* subtract shallow from deep //* Psub4: LDD 6,X SUBD 2,X ; low words STD srhi LDAB 5,X SBCB 1,X ; 3rd byte LDAA 4,X SBCA 0,X ; 4th byte BRA Pbinlongrtn //***************************************************************** //* //* lessthan4 [7..4],x < [3..0],x? //* Plt4: LDD 6,X SUBD 2,X LDAB 5,X SBCB 1,X LDAA 4,X SBCA 0,X BLT Ppushtrue4 BRA Ppushfalse4 //***************************************************************** //* //* greaterthan4: subtract in reverse order and test //* for less than (because the Z bit isn't //* valid at the end of the subtraction.) //* Pgt4: LDD 2,X SUBD 6,X LDAB 1,X SBCB 5,X LDAA 0,X SBCA 4,X BLT Ppushtrue4 BRA Ppushfalse4 //***************************************************************** //* //* equal4 ? //* Pequal4 LDD 6,X SUBD 2,X BNE Ppushfalse4 LDD 4,X SUBD 0,X BNE Ppushfalse4 Ppushtrue4: LDAB #6 ABX LDD #1 STD 0,X RTS Ppushfalse4: LDAB #6 ABX LDD #0 STD 0,X RTS //***************************************************************** //* //* longword multiplication //* //* a b c d == 4,X to 7,X //* x A B C D == 0,X to 3,X //* ------------- //* |Da Db Dc Dd //* Ca |Cb Cc Cd //* Ba Bb |Bc Bd //* Aa Ab Ac |Ad //* --------------------- //* | //* //* stores computed answer in srhi-srlo-sr2hi-sr2lo //* Pmult4: //* convert args to positive, keeping track of sign in signbit CLR signbit JSR Pabs4 PSHX LDAB #4 ABX JSR Pabs4 LDX #0 STX srhi PULX //* 4th column LDAA 3,X ; D LDAB 7,X ; d MUL STD sr2hi //* 3rd column LDAA 3,X ; D LDAB 6,X ; c MUL ADDD srlo STD srlo LDAA srhi ADCA #0 STAA srhi LDAA 2,X ; C LDAB 7,X ; d MUL ADDD srlo STD srlo LDAA srhi ADCA #0 STAA srhi //* 2nd column LDAA 3,X ; D LDAB 5,X ; b MUL ADDD srhi STD srhi LDAA 2,X ; C LDAB 6,X ; c MUL ADDD srhi STD srhi LDAA 1,X ; B LDAB 7,X ; d MUL ADDD srhi STD srhi //* 1st column //* LDAA 3,X ; D //* LDAB 4,X ; a LDD 3,X MUL ADDB srhi STAB srhi LDAA 2,X ; C LDAB 5,X ; b MUL ADDB srhi STAB srhi LDAA 1,X ; B LDAB 6,X ; c MUL ADDB srhi STAB srhi LDAA 0,X ; A LDAB 7,X ; d MUL ADDB srhi STAB srhi //* done LDAB #4 ABX LDD sr2hi STD 2,X LDD srhi STD 0,X TST signbit BEQ Pmult4done BSR Pneg4 Pmult4done RTS //*********************** //*** UNARY OPERATIONS //*********************** //***************************************************************** //* //* longword negation: result = 0 - arg //* Pneg4 LDD #0 SUBD 2,X ; low word STD srhi LDAB #0 SBCB 1,X LDAA #0 SBCA 0,X STD 0,X LDD srhi STD 2,X RTS //***************************************************************** //* //* Pabs4 negates if necessary and complements signbit //* Pabs4: TST 0,X BPL Pabs4done BSR Pneg4 COM signbit Pabs4done RTS //***************************************************************** //*** *** //*** STACK AND MEMORY OPERATIONS *** //*** *** //***************************************************************** //***************************************************************** //* //* push2(immediate) ==> stack //* Ppush2 LDD 0,Y incy2pushd INY incy1pushd INY pushd DEX DEX STD 0,X RTS //***************************************************************** //* //* push4(immediate) ==> stack //* //* push4 aa bb cc dd prev bottom of stack //* dd //* aa is highest; cc //* dd is lowest. bb //* aa //* [SP after]==> .... Ppush4: LDD 2,Y DEX DEX STD 0,X LDD 0,Y INY INY INY INY BRA pushd //***************************************************************** //* //* pushblock //* //* takes inline byte count; then data //* Ppushblock: LDAB 0,Y ; count of bytes ABY ; point at end of data (1st byte //* to be pushed pb_loop LDAA 0,Y DEX STAA 0,X DEY DECB BNE pb_loop //* Y is pointing at count byte again LDAB 0,Y ABY RTS //***************************************************************** //* //* peeki1(immediate addr) ==> word on stack //* Ppeeki1 PSHX LDX 0,Y LDAB 0,X PULX CLRA BRA incy2pushd //***************************************************************** //* //* peeki4(immediate addr) //* Ppeeki4 XGDX ; store X in D LDX 0,Y ; get addr into X LDX 2,X ; get l.s. word of value XGDX ; put in D DEX DEX STD 0,X ; push ls word on stack //* //* BRA hpeeki2 ; push ms word //* //***************************************************************** //* //* peeki2(immediate addr) //* Ppeeki2 XGDX ; store X in D LDX 0,Y LDX 0,X XGDX ; retrieve X; put peeked val in D BRA incy2pushd //***************************************************************** //* //* speek4: peek4(stack_pointer + 8-bit offset) //* Pspeek4 LDAB 0,Y ; get stack offset from inline code PSHX ABX ; add offset and stack pointer LDD 0,X ; peek m.s. word STD srhi ; save it LDD 2,X ; peek l.s. word PULX DEX DEX STD 0,X LDD srhi BRA incy1pushd //***************************************************************** //* //* speek2: peek2(stack_pointer + 8-bit offset) //* Pspeek2 LDAB 0,Y ; get stack offset from inline code PSHX ABX ; add offset and stack pointer LDD 0,X ; peek PULX BRA incy1pushd //***************************************************************** //* //* pokei1: pop word off stack and store low byte at //* immediate address in pcode Ppokei1 PSHX LDAA 1,X ; load low byte of data from stack LDX 0,Y ; load address from inline code STAA 0,X ; poke BRA pokerecover //***************************************************************** //* //* pokei2: pop word off stack and store at //* immediate address in pcode Ppokei2 PSHX LDD 0,X ; load data from stack LDX 0,Y ; load address from inline code STD 0,X ; poke pokerecover INY ; increment past inline argument pulxinyinx2 PULX inyinx2 INY incx2 INX ; pop 2 bytes INX RTS //***************************************************************** //* //* pokei4: pop two words off stack and store at //* immediate address in pcode Ppokei4 PSHX LDD 0,X ; load m.s. word from stack STD srhi ; save it LDD 2,X ; load l.s. word from stack LDX 0,Y ; load address from inline code STD 2,X ; poke l.s. word LDD srhi STD 0,X ; poke m.s. word PULX LDAB #4 ABX ; pop 4 bytes ASRB ABY ; increment past inline arg RTS //***************************************************************** //* //* spoke4: (SP + inline_offset_byte - 4) => stack_longword //* spoke4(4) replaces next to top of stack with top of stack Pspoke4 LDD 0,X ; get m.s. word STD srhi ; save it LDD 2,X ; get l.s. word STAB signbit ; save B LDAB 0,Y ; get offset from inline PSHX ABX LDAB signbit STD 2,X ; store l.s. word LDD srhi STD 0,X ; store m.s. word PULX INX INX ; pop half of poked longword BRA inyinx2 ; inc past inline, pop other 1/2 of l.w. //***************************************************************** //* //* spoke2: (stack_pointer + inline_offset_byte - 2) <= stack_data //* spoke2(2) replaces next to top of stack with top of stack Pspoke2 LDD 0,X ; get data from stack STAB srlo LDAB 0,Y ; load offset from inline code PSHX ABX LDAB srlo ; get data again STD 0,X ; store in stack location BRA pulxinyinx2 //***************************************************************** //* //* poke1 takes stack //* addr. //* word //* //* pokes low byte of word into address specified Ppoke1: LDAA 1,X ; get low byte PSHX LDX 2,X ; get addr to poke at BEQ pokeerror STAA 0,X PULX BRA Ppop4 ; pop word and addr //***************************************************************** //* //* bitset takes stack //* addr. //* word //* //* sets bits from low byte of word into address specified Pbitset: LDAA 1,X ; get low byte STAA Pbitsetop+2 ; poke byte into BSET opcode PSHX LDX 2,X ; get addr to poke at BEQ pokeerror Pbitsetop: BSET 0,X $ff PULX BRA Ppop4 ; pop word and addr //***************************************************************** //* //* bitclr takes stack //* addr. //* word //* //* clears bits from low byte of word into address specified Pbitclr: LDAA 1,X ; get low byte STAA Pbitclrop+2 ; poke byte into BCLR opcode PSHX LDX 2,X ; get addr to poke at BEQ pokeerror Pbitclrop: BCLR 0,X $ff PULX BRA Ppop4 ; pop word and addr //***************************************************************** //* //* poke2: takes stack //* addr. //* word Ppoke2 LDD 0,X ; get byte PSHX LDX 2,X ; get addr to poke at BEQ pokeerror STD 0,X PULX Ppop4 INX INX ; pop word INX INX ; pop addr RTS //***************************************************************** //* //* poke4: takes stack //* addr. //* longword Ppoke4 LDD 0,X ; get m.s. word STD srhi ; save it LDD 2,X ; get l.s. word PSHX LDX 4,X ; get addr to poke at BNE poke4ok pokeerror //* generate null pointer error PULX LDAA #PSTAT_NULLPOINTER JMP pcode_error_exit poke4ok STD 2,X ; poke l.s. word LDD srhi STD 0,X ; poke m.s. word PULX INX INX ; pop 1/2 of longword BRA Ppop4 ; pop other 1/2 of longword, addr //***************************************************************** //* //* poke1nopop: takes stack //* addr. //* word //* //* pokes low byte of word into address specified //* leaves word on stack when done Ppoke1nopop LDD 0,X ; get word PSHX LDX 2,X ; get addr to poke at BEQ pokeerror STAB 0,X ; poke low byte PULX STD 2,X ; store word where addr is now INX INX RTS ; pop word and return //***************************************************************** //* //* poke2nopop: takes stack //* addr. //* word //* //* leaves word on stack when done Ppoke2nopop LDD 0,X ; get word PSHX LDX 2,X ; get addr to poke at BEQ pokeerror STD 0,X PULX STD 2,X INX INX RTS //***************************************************************** //* //* poke4nopop takes stack //* addr. //* longword //* //* leaves longword on stack when done Ppoke4nopop: LDD 0,X ; get ms word STD srhi ; save it LDD 2,X ; get ls word PSHX LDX 4,X ; get addr to poke at BEQ pokeerror STD 2,X ; poke ls word LDD srhi STD 0,X ; poke ms word PULX LDD 2,X ; get ls word STD 4,X ; move up two bytes LDD srhi ; get ls word STD 2,X ; move up two bytes INX INX ; pop addr RTS //***************************************************************** //* //* peek1 takes addr. on stack //* //* writes peeked byte as word Ppeek1: PSHX LDX 0,X ; get addr to peek at BEQ pokeerror LDAB 0,X ; get byte in low of D CLRA PULX ; restore SP STD 0,X RTS //***************************************************************** //* //* peek2 takes addr. on stack //* Ppeek2: PSHX LDX 0,X ; get addr to peek at BEQ pokeerror LDD 0,X ; get word PULX ; restore SP STD 0,X RTS //***************************************************************** //* //* peek4 takes addr. on stack //* Ppeek4: PSHX LDX 0,X ; get addr to peek at BEQ pokeerror LDD 0,X ; get m.s. word STD srhi ; save it LDD 2,X ; get l.s. word PULX ; restore SP DEX DEX STD 2,X ; push l.s. word LDD srhi STD 0,X ; push m.s. word RTS //***************************************************************** //*** *** //*** FLOW OF CONTROL OPERATIONS *** //*** *** //***************************************************************** //***************************************************************** //* //* jumpi pc= inline_address //* Pjumpi: LDY 0,Y RTS //***************************************************************** //* //* jump pc= stack_address //* Pjump: LDY 0,X INX INX RTS //***************************************************************** //* //* jfalse: pc= inline_address if stack_boolean=0 //* Pjfalse LDD 0,X ; load stack boolean BNE incx2y2 ; if boolean <> 0 then don't jump;' hcjump LDY 0,Y INX INX RTS incx2y2 INY INY INX INX RTS //***************************************************************** //* //* jtrue: pc= inline_address if stack_boolean<>0 //* Pjtrue LDD 0,X ; load stack boolean BNE hcjump ; if boolean <> 0 then jump BRA incx2y2 //***************************************************************** //* //* jptrue: pc= inline_address if stack_boolean<>0 //* also push 1 if stack_boolean<>0 Pjptrue LDD 0,X BEQ incx2y2 LDD #1 STD 0,X LDY 0,Y RTS //***************************************************************** //* //* jpfalse: pc= inline_address if stack_boolean==0 //* also push 0 if stack_boolean==0 Pjpfalse LDD 0,X BNE incx2y2 LDY 0,Y RTS //*************************** //*** WARNING!!!! *** //*** ENTER AT OWN RISK. *** //*** MRET CODE FOLLOWS *** //*************************** //***************************************************************** //* //* MRET!: magic return //* //* PCODE STACK BEFORE: //* //* bottom [--|--] fcn return PC //* [--|--] arg1 //* [--|--] arg2 //* top [--|--] return value //* //* PCODE STACK AFTER (mret 6): //* [--|--] return value //* PC = fcn. return //* //* PROCEDURE: //* pop the return value -> return value //* pop inline_word - 2 bytes of arguments from stack -> discard //* pop return address -> PC //* push return value back on stack //* Pmret2: LDD 0,X ; load return value STD srhi ; save return value for later LDD 0,Y ; get # of bytes to pop (want to add into X) STX sr3hi ADDD sr3hi XGDX ; result back to X LDY 0,X ; load return address into PC BEQ mret_die ; nothing to return to; halt LDD srhi ; have return value in D STD 0,X ; store back on stack (replacing return addr) RTS mret_die: //* there is nowhere to return to, so we simply die. //* dying is accomplished by poking PSTAT_DEAD into our status, //* poking BRN into pcode_branch so we will exit upon return, //* and removing ourselves from the current process list. LDX current_process LDAA #PSTAT_DEAD STAA P_STATUS,X LDAA #$21 ; BRN opcode STAA pcode_branch LDD P_PREV,X ; get _PREV addr LDY P_NEXT,X ; Y = _NEXT XGDX ; X = _PREV STX P_PREV,Y ; NEXT.PREV -> _PREV STY P_NEXT,X ; PREV.NEXT -> _NEXT RTS //***************************************************************** //* //* mret0: magic return without return value on stack //* Pmret0 LDD 0,Y ; get # of bytes to pop STX srhi ADDD srhi XGDX ; put result back into X LDY 0,X ; load return address into pcode PC BEQ mret_die INX INX ; pop return address RTS //***************************************************************** //* //* mret4: magic return with 4-byte return value //* Pmret4 LDD 0,X STD srhi LDD 2,X STD sr2hi ; store ret'n val in sr and sr2;' LDD 0,Y STX sr3hi ADDD sr3hi XGDX ; pop bytes requested LDY 2,X ; fetch return addr BEQ mret_die LDD srhi STD 0,X LDD sr2hi STD 2,X RTS //***************************************************************** //*** *** //*** STACK FROBBING *** //*** *** //***************************************************************** //***************************************************************** //* //* checkstack takes word on stack; //* signals error if at least that many //* bytes do not remain. //* Pcheckstack: STX srhi ; save current SP for easy access LDX current_process ; get ptr to process data LDD srhi ; get current SP SUBD P_STACK_LIM,X ; number of stack bytes remaining CPD 0,Y ; if less than zero, lose BLO csfail INY INY ; go past inline arg LDX srhi RTS csfail DEY ; point at erroring pcode LDX srhi ; restore LDAA #PSTAT_STACKOVERFLOW JMP pcode_error_exit //***************************************************************** //* //* addsp: adds inline signed word to SP //* //* useful for reserving large blocks of stack //* (probably for an array) Paddsp XGDX ; get current SP into D ADDD 0,Y ; add inline word XGDX ; result back into X incy2 INY INY ; incr. past inline RTS //***************************************************************** //* //* sprel: pushes SP + inline_word //* //* used to push base+offset for a local array ref Psprel PSHX PULA PULB ; copy X to D ADDD 0,Y ; add offset JMP incy2pushd //***************************************************************** //* //* setsp takes word and sets SP equal to it //* can't pop the word because SP changes //* //* Psetsp: LDX 0,X RTS //***************************************************************** //*** *** //*** PCODE REGISTERS *** //*** *** //***************************************************************** //* pop stack word into register Ploadreg: LDD 0,X STD pcoderegister0 INX INX INY ; inc past register # byte RTS //* push register onto stack Pfetchreg: LDD pcoderegister0 DEX DEX STD 0,X INY RTS //***************************************************************** //*** *** //*** ARRAYS *** //*** *** //***************************************************************** //* //* //* An array of N items: //* //* hi mem [item n-1] //* [item n-2] //* //* ... //* //* [item 2] //* [item 1] //* [item 0] //* lo mem [length_word] //* //* //* items may be 1, 2, or 4 bytes in length. //* //***************************************************************** //* //* aref1 pops //* base (2-byte abs. addr.) //* index (2-byte +offset) //* //* pushes //* addr of element //* //* or generates bounds error. Paref1: LDD 0,X ; get index into D PSHX LDX 2,X ; get ptr to base of array CPD 0,X ; (index - length); should be less than zero PULX ; doesn't change CCR;' BHS errorbounds1 areffinish ADDD 2,X ; add base to index ADDD #2 ; add offset past length STD 2,X INX INX RTS //***************************************************************** //* //* aref2: pops //* index (2-byte +offset) //* base (2-byte abs. addr.) //* pushes //* addr of element //* //* or generates bounds error. Paref2 LDD 0,X ; get index into D PSHX LDX 2,X ; get ptr to base of array CPD 0,X ; (index - length); should be less than zero PULX ; doesn't change CCR;' BHS errorbounds1 LSLD ; index * 2 BRA areffinish //***************************************************************** //* //* aref4: pops //* index (2-byte +offset) //* base (2-byte abs. addr.) //* pushes //* addr of element //* //* or generates bounds error. Paref4 LDD 0,X ; get index into D PSHX LDX 2,X ; get ptr to base of array CPD 0,X ; (index - length); should be less than zero PULX ; doesn't change CCR;' BHS errorbounds1 LSLD LSLD ; index * 4 BRA areffinish errorbounds1: JMP errorbounds //***************************************************************** //* //* aref_arb: pops //* index (2-byte +offset) //* base (2-byte abs. addr.) //* pushes //* addr of element //* size is 1 byte immediate //* or generates bounds error. Paref_arb LDD 0,X ; get index into D PSHX LDX 2,X ; get ptr to base of array CPD 0,X ; (index - length); should be less than zero PULX ; doesn't change CCR;' BHS errorbounds1 LDAA 0,Y ; load size into A INY MUL BRA areffinish //***************************************************************** //*** *** //*** FLOATING POINT *** //*** *** //***************************************************************** //**************************************************************** //* Include floating point routines here //* #include "math11routinesM.asm" //* //* //**************************************************************** //**************************************************************** //* //* floatload2: //* //* loads floating point accumulators from top of pcode stack //* //* FPACC2 <-- top of stack float //* FPACC1 <-- next to top float //* floatload2 JSR GETFPAC2 INX INX INX INX JSR GETFPAC1 RTS //**************************************************************** //* //* Pint2fl //* //* takes signed integer from stack and returns //* floating point number //* Pint2fl PSHX PSHY LDD 0,X ; arg for routine in D JSR SINT2FLT ; perform conversion PULY PULX DEX DEX BRA PUTFPAC1 ; put FPACC1 on stack and return //**************************************************************** //* //* Pfl2ascii //* //* takes floating point number, string pointer //* writes null-terminated ascii string into string //* leaves string pointer on stack //* string buffer must be 14 bytes long //* Pfl2ascii PSHX PSHY INX INX JSR GETFPAC1 ; load FPACC1 DEX DEX LDX 0,X ; string pointer JSR FLTASC ; perform conversion PULY PULX LDD 0,X STD 4,X INX INX INX INX RTS ; pop float and return //**************************************************************** //* //* Pfl2lng //* //* returns hi word in D, lo word in srhi Pfl2lng PSHX PSHY JSR GETFPAC1 JSR FLT2LNG PULY PULX BCC fl2lngok //* error; A has error code JMP pcode_error_exit fl2lngok STD 0,X LDD srhi STD 2,X RTS //**************************************************************** //* //* Plng2fl //* //* call with hi word in D, lo word in srhi //* Plng2fl PSHX PSHY LDD 2,X STD srhi LDD 0,X JSR SLNG2FLT PULY PULX JMP PUTFPAC1 //**************************************************************** //* //* Pfl2int //* //* uses FLTINT to convert float to integer ... //* rounds to nearest int. //* Pfl2int PSHX PSHY JSR GETFPAC1 JSR FLT2INT PULY PULX BCC fl2intok //* error; A has error code JMP pcode_error_exit fl2intok BRA floatpushd //**************************************************************** Pfadd: PSHX PSHY JSR floatload2 JSR FLTADD Pfpulfloatpopnoerr PULY PULX INX INX INX INX JMP PUTFPAC1 ; convert FPACC1 to stack and return //**************************************************************** Pfsub: PSHX PSHY JSR floatload2 JSR FLTSUB BRA Pfpulfloatpopnoerr //**************************************************************** Pfneg: //* flip high bit of high byte! LDAA 0,X EORA #$80 STAA 0,X RTS //**************************************************************** //* //* Pfmult //* //* multiplies two floatnums and returns product //* Pfmult PSHX PSHY JSR floatload2 JSR FLTMUL ; perform multiply BRA Pfpulfloatpop //**************************************************************** //* //* Pfdiv //* //* divides (next to top) / (top of stack) //* error if carry set; then A has error code //* Pfdiv PSHX PSHY JSR floatload2 JSR FLTDIV //* assumes need to pop one float Pfpulfloatpop PULY PULX BCC Pbinopok //* error; A has error code JMP pcode_error_exit Pbinopok INX INX INX INX JMP PUTFPAC1 ; convert FPACC1 to stack and return //**************************************************************** Pfequal PSHX PSHY JSR floatload2 JSR FLTCMP ;if Z=1, they are equal PULY PULX BNE floatpushfalse floatpushtrue LDD #1 INX INX INX INX floatpushd INX INX STD 0,X RTS floatpushfalse INX INX INX INX INX INX CLR 0,X CLR 1,X RTS Pflt PSHX PSHY JSR floatload2 JSR FLTCMP PULY PULX BMI floatpushtrue BRA floatpushfalse Pfgt PSHX PSHY JSR floatload2 JSR FLTCMP PULY PULX BGT floatpushtrue BRA floatpushfalse Pfx2y PSHX PSHY JSR floatload2 JSR FLTXTOY BRA Pfpulfloatpop //**************************************************************** //* //* Pfsqrt: //* //* square root! //* error if carry set; then A has error code //* Pfsqrt PSHX PSHY JSR GETFPAC1 JSR FLTSQR Pfpulfloat PULY PULX BCC Punaryopok //* error; A has code JMP pcode_error_exit Punaryopok JMP PUTFPAC1 ; convert FPACC1 to stack and return Pfexp PSHX PSHY JSR GETFPAC1 JSR FLTETOX BRA Pfpulfloat Pf10tx PSHX PSHY JSR GETFPAC1 JSR FLT10TX BRA Pfpulfloat Pfln PSHX PSHY JSR GETFPAC1 JSR FLTLN BRA Pfpulfloat Pflog PSHX PSHY JSR GETFPAC1 JSR FLTLGT BRA Pfpulfloat Pfatan PSHX PSHY JSR GETFPAC1 JSR FLTATAN TPA PSHA ; save CCR JSR DEG2RAD ; convert from degrees for bozotron routine PULA TAP ; restore CCR BRA Pfpulfloat Pfsin PSHX PSHY JSR GETFPAC1 JSR RAD2DEG ; convert to degrees for bozotron routine JSR FLTSIN BRA Pfpulfloat Pfcos PSHX PSHY JSR GETFPAC1 JSR RAD2DEG ; convert to degrees for bozotron routine JSR FLTCOS BRA Pfpulfloat Pftan PSHX PSHY JSR GETFPAC1 JSR RAD2DEG ; convert to degrees for bozotron routine JSR FLTTAN BRA Pfpulfloat //***************************************************************** //*** *** //*** LCD DISPLAY *** //*** *** //***************************************************************** //***************************************************************** //* //* Pprintchar: //* //* print word as char to LCD //* Pprintchar LDAA #2 LDAB 1,X JSR LCDstart INX INX RTS //***************************************************************** //* //* Pprintlcd4: //* //* pop longword off of stack and print to LCD in hex //* Pprintlcd4 JSR LCDcls LDAA 0,X ; get high byte JSR Byte2Hex2LCD LDAA 1,X ; low byte JSR Byte2Hex2LCD INX INX BRA print2nocls //***************************************************************** //* //* Pprintlcd2: //* //* pop word off of stack and print to LCD //* Pprintlcd2 JSR LCDcls print2nocls LDAA 0,X ; get high byte JSR Byte2Hex2LCD LDAA 1,X ; low byte JSR Byte2Hex2LCD INX INX RTS ; pop and finish //***************************************************************** //* //* Pprintstring //* //* pops address of null-terminated string //* Pprintstring JSR LCDcls PSHX ; save PSP LDX 0,X ; get addr of string LDAA #2 ; tell LCD to print characters Ppsloop LDAB 0,X BEQ Ppsexit JSR LCDstart ; print char INX BRA Ppsloop Ppsexit PULX INX INX RTS //***************************************************************** //* //* Pprintf print formatted string //* //* takes following on stack: //* //* ssss string ptr to formatting string //* aaaa arg1 \ //* bbbb arg2 |-- optional args: ints, floats, or //* cccc arg3 / string ptrs. //* nnnn # of arg bytes (NOT including initial fmt string) //* //* returns with zero on stack. //* //* accepts following formatting controls: //* //* "%d" decimal-printed integer //* "%x" hex-printed integer //* "%f" floating-point number //* "%s" null-terminated char string //* "%c" ascii-printed byte (low byte of int) //* "%b" binary-printed byte (low byte of int) //* //* "%" sign followed by anything else prints that character. //* //* 0x0a end-of-line (clears screen when another //* char is printed) //* //* guarantees printing up to 80 characters; a given printf that //* tries to print more than 80 characters runs the risk of the //* excess characters being truncated. //* //* uses following routines: //* //* check_printbuffer returns # of free bytes in string //* buffer in D. //* //* putchar places A in string buffer. //* returns with C set if failed. //* preserves B, X reg. //* //* putchar_always places A in string buffer, despite //* putchar claiming it's full (putchar //* always lies and saves you one char.) //* Pprintf: PSHY ; save PC STX srhi ; save SP for continued use //* check that we have 81 chars free in print buffer (80 chars + \n) JSR check_printbuffer ; returns # free in D CMPD #81 BHS pf_okay pf_exitnoexecute //* back up PC to try again later; also poke BRN //* so that somebody else gets a chance to run LDAA #$21 ; BRN opcode STAA pcode_branch LDX srhi ; restore SP PULY ; restore PC DEY ; back it up RTS pf_okay //* calc ptr to formatting string LDX srhi LDD srhi ADDD #2 ; point at args ADDD 0,X ; D -> formatting string ptr STD sr2hi SUBD #2 STD sr3hi ; sr3hi -> arglist LDX sr2hi ; X -> format string ptr LDX 0,X ; X := format string ptr INX INX ; point past string size header pf_loop: LDAA 0,X BNE pf_check_percent JMP pf_exitnormal //* check for either % or \ formatting characters pf_check_percent CMPA #'%;' BEQ pf_got_percent JMP pf_normalchar pf_got_percent //* have %, get formatting char INX LDAA 0,X ; get percented char CMPA #'f;' BNE pf_notfloat //******** print FLOAT STX sr2hi ; save fmt string ptr LDX sr3hi ; is pointing in middle of float DEX DEX ; point at beginning of float PSHX ; save JSR GETFPAC1 ; load float into FPACC1 PULX DEX DEX STX sr3hi ; back up ptr to next arg LDX #STRING_TEMP_BUFFER ; buffer for ascii conversion JSR FLTASC ; cnvt into temp buffer LDX #STRING_TEMP_BUFFER ; get buffer ptr again JMP pf_stringloop pf_notfloat CMPA #'d;' BNE pf_notdecimal //******** print DECIMAL STX sr2hi ; save fmt string ptr LDY #STRING_TEMP_BUFFER ; for composing ascii LDX sr3hi ; point at int DEX DEX STX sr3hi LDD 2,X ; get int BPL pf_deccvnt //* 2's complement D to print negative number //* put "-" in output buffer! LDAA #'-;' STAA 0,Y INY LDD #0 SUBD 2,X pf_deccvnt LDX #10 IDIV PSHB ; dig 0 XGDX LDX #10 IDIV PSHB ; dig 1 XGDX LDX #10 IDIV PSHB ; dig 2 XGDX LDX #10 IDIV PSHB ; dig 3 XGDX LDX #10 IDIV PSHB ; dig 4 //* now pull them off stack and place in string buffer (cvting to ascii). //* A=1 initially meaning drop a leading zero if you find it (then clear A). //* transfer Y to X and use X as string ptr PSHY PULX LDAA #1 PULB TSTB ; set regs BEQ pf_dig3 CLRA ; got non-zero dig ADDB #'0;' STAB 0,X INX pf_dig3: PULB TSTA BEQ pf_printdig3 TSTB ; set regs BEQ pf_dig2 CLRA ; got non-zero dig pf_printdig3: ADDB #'0;' STAB 0,X INX pf_dig2: PULB TSTA BEQ pf_printdig2 TSTB ; set regs BEQ pf_dig1 CLRA ; got non-zero dig pf_printdig2: ADDB #'0;' STAB 0,X INX pf_dig1: PULB TSTA BEQ pf_printdig1 TSTB ; set regs BEQ pf_dig0 CLRA ; got non-zero dig pf_printdig1: ADDB #'0;' STAB 0,X INX pf_dig0: PULB ; print this one always ADDB #'0;' STAB 0,X INX CLRB STAB 0,X ; terminating zero. //* now print it LDX #STRING_TEMP_BUFFER BRA pf_stringloop pf_notdecimal: CMPA #'x;' BNE pf_nothex //******** print hex STX sr2hi ; save ptr to format string LDX sr3hi ; get arglist ptr DEX DEX STX sr3hi ; advance arglist ptr; 2,X is integer to fmt. LDAA 2,X ; high byte JSR byte2hexascii ; A = hi nybble, B = lo JSR putchar BCS pf_exitoverflow TBA JSR putchar BCS pf_exitoverflow LDAA 3,X ; low byte JSR byte2hexascii ; A = hi nybble, B = lo JSR putchar BCS pf_exitoverflow TBA JSR putchar BCS pf_exitoverflow BRA pf_continue pf_nothex: CMPA #'c;' BNE pf_notchar //******** print char low byte ascii STX sr2hi ; save ptr to formatting string LDX sr3hi ; get arglist ptr DEX DEX STX sr3hi ; advance arglist ptr LDAA 3,X ; get byte to format in A JSR putchar BCS pf_exitoverflow BRA pf_continue pf_notchar: CMPA #'b;' BNE pf_notbinary //******** print low byte binary STX sr2hi ; save ptr to formatting string LDX sr3hi ; get arglist ptr DEX DEX STX sr3hi ; advance arglist ptr LDAB 3,X ; get byte to format in B LDY #8 ; loop printing 8 times pf_binaryloop LSLB ; C is bit to print LDAA #'0;' ADCA #0 ; add C bit to A JSR putchar BCS pf_exitoverflow DEY BNE pf_binaryloop BRA pf_continue pf_notbinary: CMPA #'s;' BNE pf_normalchar //******** print STRING STX sr2hi ; save ptr to formatting string LDX sr3hi ; get arglist ptr DEX DEX STX sr3hi ; advance arglist ptr LDX 2,X ; get ptr to string to print INX INX ; point past string length header pf_stringloop: LDAA 0,X BEQ pf_stringloopdone JSR putchar BCS pf_exitoverflow ; exit if putchar failed INX BRA pf_stringloop pf_stringloopdone: BRA pf_continue pf_normalchar: JSR putchar ; print the character, C set if failed BCC pf_continue_norestore pf_exitoverflow: //* putchar failed; print a \n and exit printing LDAA #CR JSR putchar_always ; ha ha there is room for one more BRA pf_exitnormal pf_continue: LDX sr2hi ; restore ptr to format string pf_continue_norestore: INX ; point to next character JMP pf_loop pf_exitnormal: LDX srhi ; restore SP LDD srhi ADDD #2 ADDD 0,X ; point to string ptr XGDX ; new SP LDD #0 STD 0,X ; zero return value PULY ; restore PC RTS /*********************************/ /******* *******/ /******* SPECIAL FUNCTIONS *******/ /******* *******/ /*********************************/ //************************************************************ //* //* Pbench returns integer equalling number //* of machine cycles executing in //* 1 millisecond of real time //* //* if systemint took no time, this would be //* 1000usec * (.5 us/cycle) = 2000 Pbench: PSHX //* wait for transition of system_time_lo. LDD system_time_lo ADDD #1 benchlp1 CPD system_time_lo BNE benchlp1 //* start counting in X until system_time is again incremented ADDD #1 ; exit value LDX #0 benchlp2 INX ; 3 cycles CPD system_time_lo ; 6 cycles BNE benchlp2 ; 3 cycles //* num cycles ~= X * 12 XGDX ; put loop count in D PULX DEX DEX STD 0,X ; push on pcode stack LDD #12 DEX DEX STD 0,X ; push 13 JMP Pmult2 ; multiply and return //************************************************************ //* //* Psystime returns integer long describing //* system time //* Psystime: DEX DEX DEX DEX SEI ; disable interrupts LDD system_time_lo STD 2,X LDD system_time_hi CLI ; enable them STD 0,X RTS //********************************** //*** *** //*** Machine Language Interface *** //*** *** //********************************** //************************************************************ //* //* Pcallml //* Call machine language subroutine //* pops 2 bytes, puts in D //* pops address, JSR's to this address //* pushes D onto stack //* Pcallml: LDD 0,X INX INX PSHX PSHY LDX 0,X JSR 0,X PULY PULX STD 0,X RTS //***************************** //*** *** //*** Initialize Interrupts *** //*** *** //***************************** //************************************************************ //* //* Pinitint //* //* Initialize 6811 interrupts //* to pcode defaults //* //* turns interrupts off at start; //* exits with them on. Pinitint: SEI PSHX PSHY LDAA HPRIO ANDA #$40 ; test SMOD bit BNE *+7 LDX #$FF00 ; normal mode interrupts BRA *+5 LDX #$BF00 ; special mode interrupts PSHX ; save X for later //* store "BadInt" into all vector locations LDAB #$C0 ABX ; first vector location LDY #BadInt ini_loop STY 0,X INX INX PSHX PULA ; high byte PULA ; low byte of X TSTA BNE ini_loop PULX LDD #startup STD RESETINT,X ; reset interrupt LDD #SystemInt STD TOC4INT,X ; TOC4 interrupt #ifndef NO_BEEPER LDD #BeepInt STD TOC5INT,X ; TOC5 interrupt #endif #if defined (REV2) | defined (REV21) LDD #StopInt STD TIC1INT,X ; TIC1 interrupt #endif #ifdef REV2 LDD #IRInt STD TOC1INT,X ; TOC1 interrupt #endif //* set up interrupt vector enables //* begin by clearing them all, then setting the necessary ones LDX #BASE CLR TMSK1,X CLR TMSK2,X //* setup TIC1 interrupt on falling edge for powerdown memory save #if !(defined(BOOKBOT) | defined(HANDYBOARD)) LDAA #%00110000 #else LDAA #%00000000 #endif STAA TCTL2,X ; set for falling edge //* IC1: powerdown sequence BOOKBOT,SBOT: DISABLED YAMABICO: ENABLED //* TOC1: IR generator BOOKBOT: DISABLED YAMABICO: ENABLED //* OC4: 1 kHz system interrupt //* OC5: beeper #if defined ( BOOKBOT ) BSET TFLG1,X %00011000 ; clear pending flag BSET TMSK1,X %00011000 ; enable interrupt (not IR) #endif #if defined ( YAMABICO ) BSET TFLG1,X %10011100 ; clear pending flag BSET TMSK1,X %10011100 ; enable interrupts (incl. IR decode) #endif #if defined ( SBOT) BSET TFLG1,X %10011000 ; clear pending flag BSET TMSK1,X %00011000 ; enable interrupts (not IR xmit) #endif #if defined (HANDYBOARD) BSET TFLG1,X %00011000 ; enable sys interrupt BSET TMSK1,X %00011000 ; and beeping only #endif #if defined (REV2) | defined (REV21) BSET TFLG1,X %10011100 ; clear pending flag #ifndef NO_BEEPER BSET TMSK1,X %00011100 ; enable interrupts (not IR xmit) #else BSET TMSK1,X %00010100 ; enable everything but beeper #endif /* NO_BEEPER */ #endif PULY PULX CLI RTS //***************************************************************** //* //* PRINT BUFFER //* //* is a ring buffer. //* //* exists in memory from location //* PRINT_BUFFER //* to //* PRINT_BUFFER + PRINT_BUFFER_SIZE - 1 //* //* "print_buffer_end" points at last character inserted. //* "serial_buffer_pos" points at last character transmitted; //* (use system_status bit "SYSSTAT_IGNOREUI") //* "lcd_buffer_pos" points at last character outputted; //* (use system_status bit "SYSSTAT_NOLCD") //* //* when one of the "_buffer_pos" pointers equals "_buffer_end", //* then no characters exist in the buffer (i.e., the whole buffer //* is free). //* check_printbuffer: //* uses sr4hi, D; preserves X,Y //* returns # of chars vacant in the buffer in D. LDAA system_status ANDA #SYSSTAT_IGNOREUI BEQ cpb_check_serialbuf LDD #PRINT_BUFFER_SIZE STD sr4hi ; serial buf sez "i don't care" BRA cpb_check_lcdbuf cpb_check_serialbuf //* calc bytes left for serial output (store in sr4hi) //* CASE 1. If print_buffer_end >= serial_buffer_pos, then answer is //* PRINT_BUFFER_SIZE - (p_b_e - s_b_p) //* = PRINT_BUFFER_SIZE - p_b_e + s_b_p //* //* CASE 2. If serial_buffer_pos > print_buffer_end, then answer is //* s_b_p - p_b_e - 1 (ptrs can't hit) LDD serial_buffer_pos SUBD print_buffer_end //* if less than or = zero, then 1st case is true BLS cpb_serial_case1 //* have case 2 SUBD #1 STD sr4hi BRA cpb_check_lcdbuf cpb_serial_case1 LDD #PRINT_BUFFER_SIZE SUBD print_buffer_end ADDD serial_buffer_pos STD sr4hi cpb_check_lcdbuf LDAA system_status ANDA #SYSSTAT_NOLCD BEQ cpb_do_lcdbuf LDD #PRINT_BUFFER_SIZE ; lcd buf doesn't care;' BRA cpb_take_min ; take lesser of D and sr4hi cpb_do_lcdbuf //* //* same as serial. //* LDD lcd_buffer_pos SUBD print_buffer_end //* if less than or = zero, then 1st case is true BLS cpb_lcd_case1 //* have case 2 SUBD #1 BRA cpb_take_min cpb_lcd_case1 LDD #PRINT_BUFFER_SIZE SUBD print_buffer_end ADDD lcd_buffer_pos cpb_take_min //* lesser of D and sr4hi is answer CPD sr4hi BLS cpb_take_d LDD sr4hi ; mem is less cpb_take_d RTS //********************************************************************** //* //* putchar insert character into print buffer //* putchar_always //* //* need 2 or more chars to allow operation //* exit with carry set if operation failed //* advances serial pointer if _IGNOREUI is true //* call "putchar_always" to insert *one* char after putchar failed. //* //* preserve B, X, and Y //* putchar: PSHB ; preserve B PSHA ; save char to be printed JSR check_printbuffer CPD #2 BHS putchar_ok //* fail SEC PULA PULB RTS putchar_ok: PULA BRA putchar_continue putchar_always: //* assume there is space PSHB putchar_continue: SEI ; turn off interrupts PSHX LDX print_buffer_end //* increment ptr INX CPX #PRINT_BUFFER+PRINT_BUFFER_SIZE BNE putchar_ptrok LDX #PRINT_BUFFER putchar_ptrok: STX print_buffer_end STAA 0,X //* if _IGNOREUI is true, then check if serial ptr need to be advanced. LDAA system_status ANDA #SYSSTAT_IGNOREUI BEQ putchar_finish //* if new buffer ptr = serial ptr, advance serial. CPX serial_buffer_pos BNE putchar_finish //* increment. LDX serial_buffer_pos INX CPX #PRINT_BUFFER+PRINT_BUFFER_SIZE BNE putchar_serialptrok LDX #PRINT_BUFFER putchar_serialptrok: STX serial_buffer_pos putchar_finish: PULX ; restore me baby PULB ; restore me too CLC ; exit w/o error CLI ; enable interrupts RTS //***************************************************************** //* //* LCDinit calls LCDinit to get things going, then //* initializes the display //* //* trash D, X, and Y //* //* LCDcls clear screen on LCD //* LCDinit: BSR LCDloaddriver #if LCD_ROWS == 1 LDD #$0030 ; 8-bit operation, 1-line display #endif #if LCD_ROWS == 2 LDD #$0038 ; 8-bit operation, 2-line display #endif JSR LCDstart LDAB #%00001100 ; display on, cursor & blink off JSR LCDstart BSR LCDcharinit //* initialize various interrupt driver things LDAA #LCDSTAT_PRINTCHARCLS STAA lcd_status ; set status to ready to print LDAA #%00001000 ; set I1 STAA lcd_frobline1 CLRA STAA lcd_frobline2 LCDcls: LDD #$0001 ; home and clear screen JSR LCDstart LDAB #$06 ; set to increment char loc & cursor pos JSR LCDstart LDAB #$8F ; put cursor just to right of view window JSR LCDstart RTS //***************************************************************** //* //* LCDcharinit: loads custom characters into LCD character //* RAM. Assumes driver has been loaded.. //* LCDcharinit LDD #$0040 ; begin with character 0 JSR LCDstart LDAA #2 ; data mode LDX #LCDchardata LCDcharinitloop LDAB 0,X JSR LCDstart INX CPX #LCDchardataend BNE LCDcharinitloop RTS LCDchardata //* character 0: used for status frobbies #ifdef LCD_ROWS #ifdef LM576 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 #else //* frob character one #define HEARTBEAT_HEART /* #define HEARTBEAT_PACMAN */ /* #define HEARTBEAT_JUMPING_JACKS */ #if defined(HEARTBEAT_JUMPING_JACKS) FCB %00001110 FCB %00001110 FCB %00000100 FCB %00000100 FCB %00011111 FCB %00000100 FCB %00000100 FCB %00001010 #elif defined(HEARTBEAT_PACMAN) FCB %00000000 FCB %00000110 FCB %00001111 FCB %00011100 FCB %00011000 FCB %00011100 FCB %00001111 FCB %00000110 #elif defined(HEARTBEAT_HEART) FCB %00000000 FCB %00001010 FCB %00011111 FCB %00011111 FCB %00001110 FCB %00000100 FCB %00000000 FCB %00000000 #else FCB %00000000 FCB %00000000 FCB %00001110 FCB %00001110 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 #endif #endif #endif FCB %00000000 FCB %00000000 FCB %00010001 FCB %00010001 FCB %00010001 FCB %00001111 FCB %00000001 FCB %00001110 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00001100 FCB %00000100 FCB %00001000 FCB %00000000 FCB %00000000 FCB %00001111 FCB %00010001 FCB %00010001 FCB %00001111 FCB %00000001 FCB %00001110 FCB %00000000 FCB %00000000 FCB %00001100 FCB %00001100 FCB %00000000 FCB %00001100 FCB %00001100 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00001100 FCB %00001100 FCB %00000000 FCB %00001100 FCB %00000100 FCB %00001000 //* character #6 FCB %00000010 FCB %00000000 FCB %00000110 FCB %00000010 FCB %00000010 FCB %00000010 FCB %00010010 FCB %00001100 #ifdef LCD_ROWS #ifdef LM576 //* character #7 FCB %00011111 FCB %00000001 FCB %00000010 FCB %00000100 FCB %00001000 FCB %00001000 FCB %00001000 FCB %00000000 #else //* frob character two #if defined(HEARTBEAT_JUMPING_JACKS) FCB %00001110 FCB %00001110 FCB %00010101 FCB %00001110 FCB %00000100 FCB %00000100 FCB %00001010 FCB %00010001 #elif defined(HEARTBEAT_PACMAN) FCB %00000000 FCB %00000000 FCB %00001110 FCB %00011111 FCB %00011111 FCB %00011111 FCB %00001110 FCB %00000000 #elif defined(HEARTBEAT_HEART) FCB %00000000 FCB %00000000 FCB %00001010 FCB %00001110 FCB %00000100 FCB %00000000 FCB %00000000 FCB %00000000 #else FCB %00000000 FCB %00000000 FCB %00000000 FCB %00000000 FCB %00001110 FCB %00001110 FCB %00000000 FCB %00000000 #endif #endif #endif LCDchardataend //****************************************************************** //* //* LCDloaddriver: downloads single-char or cmd driver for LCD //* LCDloaddriver LDX #LCDdriver LDY #LCDstart LCDloop LDAA 0,X STAA 0,Y INX INY CPX #LCDenddriver BNE LCDloop RTS //****************************************************************** //* //* LCD driver: //* call routine at LCDstart //* with A=command, B=data to be output to LCD //* //* all regs are preserved LCDdriver: STX LCDtempword-LCDdriver+LCDstart LDX #$1000 SEI ; disable interrupts BCLR HPRIO,X %00100000 ; put into single chip mode BCLR PORTA,X %00010000 ; turn off LCD E line STAA LCDtempbyte-LCDdriver+LCDstart CLR DDRC,X ; make port C input //* if A is query command, just check LCD for busy-ness CMPA #LCDCMD_QUERYBUSY BNE LCDBusy LDAA #1 STAA PORTB ; read operation from LCD BSET PORTA,X %00010000 ; frob LCD on LDAA PORTC,X ; get status BCLR PORTA,X %00010000 ; frob LCD off BRA LCDdriverexit ; exit LCDBusy LDAA #1 STAA PORTB ; read operation from LCD BSET PORTA,X %00010000 ; frob LCD on LDAA PORTC,X ; get status BCLR PORTA,X %00010000 ; frob LCD off ANDA #$80 ; bit 7 is busy flag BNE LCDBusy LDAA #$FF STAA DDRC,X ; make port C output LDAA LCDtempbyte-LCDdriver+LCDstart STAA PORTB,X ; high byte is control STAB PORTC,X ; low byte is data BSET PORTA,X %00010000 BCLR PORTA,X %00010000 ; frob LCD LCDdriverexit BSET HPRIO,X %00100000 ; put into expanded chip mode LDX LCDtempword-LCDdriver+LCDstart LCDdriverCLI CLI ; enable interrupts RTS ; return to monitor command loop LCDtempword FDB 0 LCDtempbyte FCB 0 LCDenddriver //**************************************************************** //**************************************************************** //* //* byte2hexascii converts byte to its 2-char ASCII hex equiv. //* //* INPUT: byte in A register //* OUTPUT: MSB is A, LSB is B //* byte2hexascii: PSHA * store it; work on B first ANDA #$0F * get LS nybble ADDA #$30 * puts it in ASCII "0" to "?" CMPA #$3A BMI H2A1 ADDA #$07 * now it's "A" to "F" ;' H2A1 TAB * done with LSB PULA LSRA * shift that baby down LSRA LSRA LSRA * into the lower nybble position ADDA #$30 * puts it in ASCII "0" to "?" CMPA #$3A BMI H2A2 ADDA #$07 * now it's "A" to "F";' H2A2 RTS * done //**************************************************************** //* //* Byte2Hex2LCD cvts byte in A reg to hex & prints to LCD //* Byte2Hex2LCD: JSR byte2hexascii ; A=1st char, B=2nd char PSHB ; save 2nd char TAB LDAA #2 JSR LCDstart PULB ; load 2nd char JSR LCDstart RTS //**************************************************************** //* //* PrintDLCD: prints number in D to LCD //* PrintDLCD LDX #10 IDIV PSHB ; dig 0 XGDX LDX #10 IDIV PSHB ; dig 1 XGDX LDX #10 IDIV PSHB ; dig 2 XGDX LDX #10 IDIV PSHB ; dig 3 XGDX LDX #10 IDIV PSHB ; dig 4 JSR LCDstart LDD #$0001 ; home and clear screen JSR LCDstart LDD #$0007 ; set to increment char loc & cursor pos JSR LCDstart LDD #$008F ; put cursor just to right of view window JSR LCDstart LDAA #2 PULB ADDB #$30 JSR LCDstart PULB ADDB #$30 JSR LCDstart LDAB #512+'.' JSR LCDstart PULB ADDB #$30 JSR LCDstart PULB ADDB #$30 JSR LCDstart PULB ADDB #$30 JSR LCDstart LDAB #512+'m' JSR LCDstart LDAB #512+'s' JSR LCDstart RTS //**************************************************************** //* //* printstringLCD: //* clears screen and //* prints null-terminated string pointed at by X. //* assumes LCD driver is loaded. printstringLCD LDD #$0002 JSR LCDstart LDAA #2 psloop LDAB 0,X BEQ psloopdone JSR LCDstart INX BRA psloop psloopdone RTS //***************************************************************** //*** *** //*** PROCESS CONTROL *** //*** *** //***************************************************************** //***************************************************************** //* //* PROCESS MANAGER //* //* creates new processes. //* //* Call with following on current process's stack: //* //* $0000 //* \ //* ... |-- args to function call //* / //* nnnn -- number of arg bytes, incl. $0000 //* pcpc -- addr of func call //* tttt -- # of ticks/timeslice (low byte used) //* ssss -- # of stack bytes to reserve (2 bytes) //* //* All of the previous is popped from the stack. //* A 2-byte return value is generated: the process number given //* to the new process. //* //* //* How it does it: //* //* 1. Look through active processes for "best fit" of stack size //* required. //* //* generates PSTAT_NONEWSTACKSPACE error if not enough //* stack exists. //* //* 2. Find empty process slot and install process data. Link //* process into list of active processes. //* //* generates PSTAT_NOPROCSPACE error if there are no //* remaining process slots. //* //* 3. Copy stack data onto new process's stack. Return process ID. //* //* //* //* temporary storage: //* //* srhi X register (SP of current process) //* For part 1: //* sr2hi addr of proc slot found //* sr3hi best fit so far (0 = best) //* Pstartprocess: PSHY ; save PC STX srhi ; save SP for later use LDD #$7FFF STD sr3hi ; best fit for finding stack //* beginning with "UI process", look for free stack. LDX #PROCESS_TABLE mp_findstackloop LDY P_NEXT,X ; get ptr to next process CPY #PROCESS_TABLE ; = first process? BNE mp_findslotaddr ; no //* if reached 1st process, then have till rest of all stack LDD #PCODE_STACK_END ; have rest of total stack STD sr4hi BRA mp_findstacksize mp_findslotaddr LDD P_STACK_ORG,Y ; lower stack limit STD sr4hi //* Y has stack org for next proc (our limit), X points at cur proc slot mp_findstacksize LDD P_STACK_LIM,X ; end of stack, current proc SUBD sr4hi ; D has difference //* D has # of bytes between two stacks. Let's see if it's big enough //* for our uses PSHX ; save curr proc ptr LDX srhi ; get ptr to pcode stack SUBD 0,X ; subtract desired stack size PULX //* D now has amount of extra stack space if we were to use this slot. //* if this is less than zero, then this slot is too small. BMI mp_trynextslot //* //* compare this difference with previous best difference (sr3hi). //* if this one is better, store X as best slot so far, and this diff //* as new best. //* CPD sr3hi //* if greater than zero, then current error is greater than a previous //* best. Try again. BGT mp_trynextslot //* //* hooray! we found a better fit. store X as addr, D as error of //* this fit. STX sr2hi STD sr3hi //* //* X is addr of curr process slot. //* Y is addr of next process slot. //* If Y = PROCESS_TABLE, then we're done. //* mp_trynextslot CPY #PROCESS_TABLE BEQ mp_stacksearchdone PSHY PULX BRA mp_findstackloop //* //* OK, we finished! //* Did we find any stack? mp_stacksearchdone LDD sr3hi CPD #$7FFF ; pre-loaded as "best fit" BNE mp_findprocslot //* //* bad news boys and girls. There was no stack to be found. //* Exit with PSTAT_NONEWSTACKSPACE error. LDAA #PSTAT_NONEWSTACKSPACE BRA mp_exiterror mp_findprocslot //* //* this is relatively easy. We start at PROCESS_TABLE and search //* for a process slot with status "PSTAT_DEAD". LDX #PROCESS_TABLE+PROCESS_SLOT_LENGTH ; skip UI proc mp_findslotloop LDAA P_STATUS,X CMPA #PSTAT_DEAD BEQ mp_installproc ; found one XGDX ADDD #PROCESS_SLOT_LENGTH XGDX CPX #PROCESS_TABLE+PROCESS_SLOT_LENGTH*MAX_PROCESSES BNE mp_findslotloop //* //* uh oh. we didn't find one LDAA #PSTAT_NOPROCSPACE mp_exiterror: //* exit by poking the error code into our status and //* poking BRN, also decrementing our PC to point at the offending //* instruction LDX srhi ; restore SP PULY ; restore PC DEY ; back it up JMP pcode_error_exit mp_installproc //* //* 1. install new slot into proc list //* 2. copy initial vals into new proc slot //* 3. copy stack into new proc stack //* 4. exit and finish! //* //* X has ptr to new proc slot; swap it to Y PSHX PULY //* setup ptrs LDX sr2hi ; ptr to "prev" proc LDX P_NEXT,X ; ptr to "next" proc //* X -> next STY P_PREV,X ; new <- next STX P_NEXT,Y ; new -> next LDX sr2hi //* X -> prev STY P_NEXT,X STX P_PREV,Y //* X -> prev, Y -> new //* set up stack lims LDD P_STACK_LIM,X SUBD #1 STD P_STACK_ORG,Y ADDD #1 LDX srhi ; get ptr to pcode stack SUBD 0,X ; compute stack limit from stack size STD P_STACK_LIM,Y //* have set up _STACK_ORG, _STACK_LIM, _NEXT, _PREV //* setup _TICKS, _PC, _STATUS LDAA 3,X ; low byte of ticks STAA P_TICKS,Y LDD 4,X ; PC STD P_PC,Y LDAA #PSTAT_RUNNING ; initial status STAA P_STATUS,Y //* setup _ID LDD process_counter ADDD #1 STD process_counter STD P_ID,Y //* almost done. copy stack-data from running pcode stack to new stack. LDD srhi ; base of pcode stack ADDD 6,X ; # of stack bytes to copy ADDD #8 ; D now points just above 1st byte to copy LDX 6,X ; # of bytes again XGDX ; count in D, ptr in X DEX ; fix ptr STX srhi ; this will be our SP when done STY sr2hi ; save slot ptr of new process LDY P_STACK_ORG,Y ; get ptr to our new stack mp_copyloop PSHA LDAA 0,X STAA 0,Y PULA DEX DEY SUBD #1 BNE mp_copyloop //* Y has SP of new process LDX sr2hi ; get slot ptr of new proc INY ; inc to point at last byte copied STY P_SP,X ; finished setting up proc slot! LDX srhi ; SP of running proc //* return process ID DEX ; it's pointing one lower than normal,;' //* just dec once. LDD process_counter STD 0,X PULY ; pcode PC saved initially RTS //***************************************************************** //* //* Phaltnotify //* //* halts current process //* sets status byte to "halted" //* pokes BRN so process really halts. Phaltnotify: LDAA #PSTAT_HALTED ; UI halt doesn't wedge system;' BRA pcode_error_exit //***************************************************************** //* //* Pkillprocess //* //* pops PID number off of stack //* finds the process in question and kills it. //* //* if this is a suicide (current_process == addr of PID), //* then pokes the BRN opcode into the schedule so process //* doesn't keep executing. //* //* returns 0 if the process was found and killed; //* 1 if the process could not be located. //* Pkillprocess: PSHX ; save SP LDD 0,X ; get PID STD srhi ; save it for compare LDX #PROCESS_TABLE kp_search: LDD P_ID,X ; get PID into D CPD srhi BEQ kp_found LDX P_NEXT,X ; get next process in table CPX #PROCESS_TABLE BNE kp_search //* process not found PULX LDD #1 STD 0,X ; put return value on stack RTS kp_found: //* X has process ptr CPX current_process BNE kp_notsuicide //* suicide: use mret_die code to die PULX ; pop val off of stack JMP mret_die kp_notsuicide: PSHY ; save Y, we're going to trash it;' LDAA #PSTAT_DEAD STAA P_STATUS,X LDAA #$21 ; BRN opcode STAA pcode_branch LDD P_PREV,X ; get _PREV addr LDY P_NEXT,X ; Y = _NEXT XGDX ; X = _PREV STX P_PREV,Y ; NEXT.PREV -> _PREV STY P_NEXT,X ; PREV.NEXT -> _NEXT PULY ; restore PC PULX ; restore SP LDD #0 STD 0,X ; return value RTS //***************************************************************** //* //* Pdefer //* //* pokes BRN instruction into pcode runner so that //* current process exits immediately //* Pdefer LDAA #$21 ; BRN opcode STAA pcode_branch RTS //***************************************************************** //*** *** //*** ERRORS *** //*** *** //***************************************************************** Pundefined EQU 0 ; opcode for compiler use //* //* NOTES ABOUT ERROR HANDLING //* //* When a run-time error happens, the interpreter "backs up" the machine //* to the state of things just before the offending pcode instruction //* was executed (the PC should point at the instruction that caused the //* error). It pokes an error condition code into the status byte for the //* process causing the error, and then RTS's. //* //* errorbounds: DEY ; back up PC LDAA #PSTAT_ARRAYBOUNDS BRA pcode_error_exit //********************************************************************** //* //* pcode_error_exit //* //* call with //* A = error code, //* X = original SP, and //* Y = pointing at the instruction that provoked the error. //* //* exit by poking the error code into our status and //* poking BRN, also decrementing our PC to point at the offending //* instruction //* //* also, print message to LCD screen indicating that a //* runtime error has occured //* pcode_error_exit: PSHX ; save SP LDX current_process ; get ptr to our slot TSTA BNE pex_errorok LDAA #PSTAT_UNKNOWN_ERROR pex_errorok: STAA P_STATUS,X ; store error code STAA srlo ; save error code for print msg LDAA #$21 ; BRN opcode STAA pcode_branch ; poke so that pcode exits immediately LDAA srlo CMPA #PSTAT_HALTED BHS pex_exit LDX #runtime_error_msg pex_loop LDAA 0,X BEQ pex_loopdone JSR putchar BCS pex_loop ; if putchar failed, keep trying INX BRA pex_loop pex_loopdone //* some gross code to print srlo as a number from 0 to 99 decimal CLRA LDAB srlo pex_deccvt SUBB #10 BLO pex_a_ok INCA BRA pex_deccvt pex_a_ok ADDB #10 ; B is ls. digit, A is m.s. ADDA #'0 ; cvt to ascii;' JSR putchar BCS *-3 ; loop till printed A TBA ADDA #'0;' JSR putchar BCS *-3 LDAA #CR ; print carriage return JSR putchar BCS *-3 pex_exit PULX ; restore SP RTS runtime_error_msg FCB 0x0a ; CR to clear screen FCC 'RUNTIME ERR ' FCB 0 //***************************************************************** //*** *** //*** INTERRUPT ROUTINES *** //*** *** //***************************************************************** //****************************************************************************** //* //* IRInt toggle output on beeper line //* //* TIMER: uses TOC1 //* IRInt: LDX #$1000 * point to register base LDD IRtone ADDD TOC1,X * add TOC1 to D STD TOC1,X * store back BCLR TFLG1,X %01111111 * clear OC1 for next compare LDAA PORTA EORA #%10000000 STAA PORTA ; toggle IR output pin RTI //* #ifndef NO_BEEPER //****************************************************************************** //* //* BeepInt: toggle output on beeper line //* //* TIMER: uses TOC5 //* BeepInt LDX #$1000 * point to register base LDD beeptone ADDD TI4O5,X * add TOC5 to D STD TI4O5,X * store back BCLR TFLG1,X %11110111 * clear OC5 for next compare RTI //* #endif //****************************************************************************** //* //* SystemInt 1 kHz system interrupt routine //* //* TIMER: uses TOC4 for control //* //* //* System interrupt performs the following tasks: //* //* 0. sets up for next interrupt //* 1. increment system time //* 2. decrement "process_ticks". If zero, pokes //* BRN (branch never) into pcode_run loop, so that //* current process exits. //* 3. deals with LCD print. //* 4. does PWM stuff. //* 5. does shaft encoder stuff. //* PRINTBUFFER EQU %00000001 IRDECODE EQU %00000010 PWM EQU %00000100 SHAFTENCODER EQU %00001000 SystemInt: LDX #$1000 * point to register base #ifdef SBOT //* setup to read analog 7 (battery level) LDAA #%00000111 STAA ADCTL,X #endif //* setup for next interrupt LDD #2000 ; 2000 cycles = 1 millisec. ADDD TOC4,X * add TOC5 to D STD TOC4,X * store back BCLR TFLG1,X %11101111 * clear OC4 for next compare //* turn on some interrupts LDAA TMSK1,X PSHA ; save interrupt mask #ifdef SBOT ANDA #%10001000 ; OC1, OC5 enabled #endif #ifdef REV2 ANDA #%10001100 ; OC1, TIC1 enabled, OC5 enabled #endif #ifdef REV21 ANDA #%10101100 ; OC1, IC1, OC5, OC3 enabled #endif #ifdef HANDYBOARD ANDA #%00001000 #endif STAA TMSK1,X CLI ; locally enable interrupts //* increment system time LDX system_time_lo INX STX system_time_lo BNE si_noinc_timehi LDX system_time_hi INX STX system_time_hi si_noinc_timehi #ifndef SBOT_SHUTDOWN_VOLTAGE #define SBOT_SHUTDOWN_VOLTAGE 130 #endif #ifdef SBOT //* check if battery level is > 130 LDAA ADR1 CMPA #SBOT_SHUTDOWN_VOLTAGE BLS si_boardok si_boarddie LDX #$1000 //* sbot shutdown sequence //* turn on IR reflectance lamp BSET PORTA,X $20 //* turn off motors LDAA #MOTORS_OFF STAA DIGOUTPUT //* halt processor CLRA TAP STOP si_boardok EQU * #endif //* do pcode process ticks stuff DEC process_ticks BNE si_lcdcheckbusy //* store BRN in pcode_branch LDAA #$21 ; BRN opcode STAA pcode_branch si_lcdcheckbusy #if defined (LCD_ROWS) || defined (LCD_COLS) //* check if LCD is busy LDAA #LCDCMD_QUERYBUSY JSR LCDstart ; high bit of A set if busy ANDA #$80 BEQ si_checkfrob JMP si_lcd_done ; come back later si_checkfrob //* if (system_time MOD 64 is 0) and (lcd_status= _PRINTCHAR or _PRINTCHARCLS), //* do for frobby stuff. LDAA system_time_lo+1 ; lowest byte ANDA #%00111111 BNE si_checkforprint LDAA lcd_status CMPA #LCDSTAT_PRINTCHARCLS BHI si_checkforprint //* prior to frob, modify froblines based on IR sensing (if //* IR decoding is enabled) #ifdef LM576 //* have the weird LCD with status blips on character 15 LDAA system_functions ANDA #IRDECODE BEQ si_frobout LDX #IRdetect0 LDAA IR_DETECT,X CMPA #IR_DETECT_THRESHOLD ; for LCD BLS frob_bit2_off LDAA lcd_frobline2 ORAA #%00000010 BRA IR_storego1 frob_bit2_off LDAA lcd_frobline2 ANDA #%11111101 IR_storego1 STAA lcd_frobline2 LDAA IR_DETECT+1,X CMPA #IR_DETECT_THRESHOLD ; for LCD BLS frob_bit1_off LDAA lcd_frobline2 ORAA #%00000001 BRA IR_storego2 frob_bit1_off LDAA lcd_frobline2 ANDA #%11111110 IR_storego2 STAA lcd_frobline2 LDAA IR_DETECT+2,X CMPA #IR_DETECT_THRESHOLD ; for LCD BLS frob_bit0_off LDAA lcd_frobline1 ORAA #%00000001 BRA IR_storeexit frob_bit0_off LDAA lcd_frobline1 ANDA #%11111110 IR_storeexit STAA lcd_frobline1 si_frobout: SEI LDD #$0040 ; set CG RAM address to 0 JSR LCDstart LDAA #LCDCMD_ASCII LDAB lcd_frobline1 ; frob line 1 data JSR LCDstart LDAB lcd_frobline2 ; frob line 2 data JSR LCDstart LDD #$0045 ; set CG RAM address to 5 JSR LCDstart LDAA lcd_status BEQ si_lcd_setcursor JMP si_lcd_done ; if not zero, next char causes CLS anway si_lcd_setcursor CLRA ; LCD command LDAB lcd_char_count ; count of chars = cursor pos ORAB #%10000000 ; set DD RAM address to cursor pos JSR LCDstart CLI JMP si_lcd_done #endif #if defined (LCD_ROWS) && ! defined (LM576) //* have an LCD, but it's not the weird Rev 2 one //* swap char 0 and char 7 in DD RAM pos'n 63+LCD_COLS SEI CLRA LDAB #$80+63+LCD_COLS JSR LCDstart LDD scheduler_iterations CMPD #100 BLT si_leave_frob LDD #0 STD scheduler_iterations LDAA lcd_frobline1 EORA #%00001100 ; xora I1 and I3 STAA lcd_frobline1 si_leave_frob LDAA lcd_frobline1 ANDA #%00001000 BEQ si_char7 LDAB #0 BRA si_insertchar si_char7 LDAB #7 si_insertchar LDAA #LCDCMD_ASCII JSR LCDstart CLRA ; LCD command LDAB lcd_char_count ; count of chars = cursor pos //* chars 8-15 are in DD RAM pos'ns 64-71 (Optrex model 16117A 16x1 screen) //* if cursor pos is num of cols, advance by (64-# of cols) CMPB #LCD_COLS BLO si_ddposok ADDB #64-LCD_COLS si_ddposok ORAB #%10000000 ; set DD RAM address to cursor pos JSR LCDstart si_nofrob CLI JMP si_lcd_done #endif si_checkforprint //* check if printing is enabled LDAA system_functions ANDA #PRINTBUFFER BNE si_checkcls ; if zero, skip printing JMP si_lcd_done //* check if we are in middle of LCD clear screen. If so, process si_checkcls LDAA lcd_status CMPA #LCDSTAT_CLS2 BHS si_lcdstat3 //* check if we need to print a character to the LCD LDX lcd_buffer_pos CPX print_buffer_end BEQ si_lcd_done ; nope //* we must be in _PRINTCHAR or _PRINTCHARCLS mode. //* increment buffer ptr, and fetch char to be printed in B. INX CPX #PRINT_BUFFER+PRINT_BUFFER_SIZE BNE si_lcdbufptrok LDX #PRINT_BUFFER si_lcdbufptrok STX lcd_buffer_pos LDAB 0,X ; get char into B //* okay, what state is the lcd in? LDAA lcd_status CMPA #LCDSTAT_PRINTCHAR BNE si_lcdstat2 //* print normal char CMPB #CR ; have CR? BNE si_lcdputchar LDAA #LCDSTAT_PRINTCHARCLS ; set LCD status to clear on next char STAA lcd_status BRA si_lcd_done si_lcdputchar //* check number of chars since last newline. If > num of chars //* on screen -1, punt printing it. LDAA lcd_char_count CMPA #LCD_ROWS*LCD_COLS-1 BEQ si_lcd_done ; punt INCA STAA lcd_char_count ; inc counter #if LCD_ROWS == 2 //* if we're on char 8, advance cursor pos to 64 (Optrex 16117A LCD) //* if on char LCD_COLS+1, advance CMPA #1+LCD_COLS BNE si_lcd_noadv CLRA ; LCD command PSHB ; save character to be printed LDAB #$C0 ; position cursor JSR LCDstart PULB si_lcd_noadv EQU * #endif si_translate_and_print CMPB #$20 BLO si_lcdputcharlcd ; if less than $20, put it CMPB #$7F BHI si_lcdputcharlcd ; if > than $7F, put it LDX #LCD_translation_table SUBB #$20 ABX LDAB 0,X ; translate character si_lcdputcharlcd LDAA #LCDCMD_ASCII ; control for normal char JSR LCDstart BRA si_lcd_done si_lcdstat2 CMPA #LCDSTAT_PRINTCHARCLS BNE si_lcdstat3 //* want to initiate clear screen sequence when we get a character //* from this state. save char to be printed in "lcd_savedchar". //* it will get printed when _FINISHCLS state is reached. STAB lcd_savedchar ; save real char LDD LCD_clsops ; home and clear screen code JSR LCDstart LDAA #LCDSTAT_CLS2 ; setup next operation STAA lcd_status BRA si_lcd_done si_lcdstat3 CMPA #LCDSTAT_FINISHCLS ; last state BEQ si_lcdfinishcls //* pull lcd cmd from list LDX #LCD_clsops ; point to table LDAB lcd_status ; get status ABX ; add to ptr LDD 0,X ; get op'n;' JSR LCDstart LDAA lcd_status ADDA #2 STAA lcd_status ; increment status by 2 BRA si_lcd_done si_lcdfinishcls //* the clear screen is done. now we print the char saved. LDAA #LCDSTAT_PRINTCHAR STAA lcd_status LDAA #1 STAA lcd_char_count ; set # of chars/line printed LDAB lcd_savedchar //* if = 10, then it's a CR. ignore. CMPB #CR BNE si_translate_and_print si_lcd_done: EQU * #endif #ifdef BOOKBOT //* PWM code for bookbot *********************************** //* //* motor 0 is enabled by bit 4 of PORTA; //* motor 1 is enabled by bit 5 of PORTA. //* //* use "speeda" and "speedb" bytes as masks for enables. //* check if pulse width modulation is enabled LDAA system_functions ANDA #PWM BEQ SPDexit ; if zero, exit w/o modulation LDX #$1000 LDAA speeda ; rotate bits in motor speed ASLA BCC SPDOnA BSET PORTA,X #%00010000 ADDA #1 BRA SPDsetA SPDOnA: BCLR PORTA,X #%00010000 SPDsetA STAA speeda LDAA speedb ; rotate bits in motor speed ASLA BCC SPDOnB BSET PORTA,X #%00100000 ADDA #1 BRA SPDsetB SPDOnB: BCLR PORTA,X #%00100000 SPDsetB STAA speedb SPDexit EQU * //*************************** end of bookbot PWM code #else #if defined (SBOT) | defined (REV21) | defined (HANDYBOARD) //* PWM code for sensor robot and Rev 2.1 Board *****************8 //* //* motor direction selected by low nybble of DIGOUTPUT //* motors enabled by high nybble of DIGOUTPUT //* The original code would have set all the motor on bits //* to begin with, EOR'd the corresponding bits for any //* pulses found, to set them to 0, then Inverted the //* motor on bits at the end. //* //* The Smooth-PWM algorithm (C)Skids 1997 is based on the //* idea of setting the pulses for some fraction (x/256) of the time. //* So, the total number of pulses after t ticks will have been //* Pulses(t) = INT(tx/256), //* which is equivalent to: //* Pulses(t) = (tx- tx MOD 256)/256. //* tx MOD 256 is equivalent to the lower byte of tx, and (tx-tx MOD 256)/256 //* is the upper byte. We output a new pulse whenever Pulses(t)-Pulses(t-1) //* changes, i.e. whenever the upper byte changes. The easiest way to //* calculate tx is to add x on every new time interval and the upper //* byte will have changed (there will be a pulse) whenever there //* is a carry out from the lower byte of the addition. //* gSpeedFrac[i] contains tx[i] MOD 256, (the lower byte). //* speed[i] contains x[i]. //* check if pulse width modulation is enabled LDAB motor LDAA system_functions ANDA #PWM BEQ SPDexit ; if zero, exit w/o modulation ;dcbaDCBA is the output. All motor off bits should be 0. LDAB #255 LDAA speeda ADDA gSpeedFracA ;Calc new tx MOD 256 into A STAA gSpeedFracA ;and the pulse into carry flag. RORB ;Just rotate each pulse into bit 7. LDAA speedb ADDA gSpeedFracB STAA gSpeedFracB RORB ;they eventually get rotated into LDAA speedc ADDA gSpeedFracC STAA gSpeedFracC RORB ;their correct positions. LDAA speedd ADDA gSpeedFracD STAA gSpeedFracD RORB ;[Motor3][Motor2][Motor1][Motor0].... ANDB motor ;All motor off bits should be 0s SPDexit STAB DIGOUTPUT //*************************** end of sensor robot PWM code #else //*************************************************** //* SpeedInt: does motor speed control //* uses TOC2 interrupt running at 1kHz rate (RTI is too slow) //* code for Rev 2 board //* //* Randy's code -- speed byte contains bits to be rotated through //* motor //* //* begin speed control stuff //* check for all motors being off and exit early LDAB motor BEQ SPDexitnofetch ; if off, poke it and exit //* check if pulse width modulation is enabled LDAA system_functions ANDA #PWM BEQ SPDexitnofetch ; if zero, exit w/o modulation #ifdef (SMOOTHPWM) //* In this hardware, the motor bits are paired, //* and both bits in the pair will be set to 1 //* if the motor should be on, otherwise they //* will be set to ~motor. Code space is approx 11b*4+3 for Randy's. //* This one will be 10b*4+3 LDAA speeda ADDA gSpeedFracA STAA gSpeedFracA BCC NoAPulse: ANDB #%11111100 NoAPulse: LDAA speedb ADDA gSpeedFracB STAA gSpeedFracB BCC NoBPulse: ANDB #%11110011 NoBPulse: LDAA speedc ADDA gSpeedFracC STAA gSpeedFracC BCC NoCPulse: ANDB #%11001111 NoCPulse: LDAA speedd ADDA gSpeedFracD STAA gSpeedFracD BCC NoDPulse: ANDB #%00111111 NoDPulse: SPDexitnofetch: COMB STAB DIGOUTPUT #else LDAA speeda ; rotate bits in motor speed ASLA BCC SPDOnA ANDB #%11111100 SPDOnA: ADCA #0 STAA speeda LDAA speedb ; rotate bits in motor speed ASLA BCC SPDOnB ANDB #%11110011 SPDOnB: ADCA #0 STAA speedb LDAA speedc ; rotate bits in motor speed ASLA BCC SPDOnC ANDB #%11001111 SPDOnC: ADCA #0 STAA speedc LDAA speedd ; rotate bits in motor speed ASLA BCC SPDOnD ANDB #%00111111 SPDOnD: ADCA #0 STAA speedd SPDexitnofetch: COMB STAB DIGOUTPUT #endif #endif #endif #ifdef SBOT //* check if IR decoding is enabled LDAA system_functions ANDA #IRDECODE BEQ IR_exit //* do IR decoding //* IR_setup: LDY #IR_jumptable125 LDAA EXPANSION_INPUTS ANDA #$10 ; switch 6 of expansion board BEQ IR_go LDY #IR_jumptable100 //**************** IR_go: LDAA PORTD ANDA #IR_SENSOR0 LDX #IRdetect0 PSHY JSR IR_doitbaby PULY LDAA PORTD ANDA #IR_SENSOR1 INX ; LDX #IRdetect1 PSHY JSR IR_doitbaby PULY LDAA PORTD ANDA #IR_SENSOR2 INX ; LDX #IRdetect2 PSHY JSR IR_doitbaby PULY IR_exit: //* check if shaft encoding is enabled LDAA system_functions ANDA #SHAFTENCODER BEQ shaft_exit //* do shaft encoder stuff LDX #scounter0 LDAB EXPANSION_INPUTS ANDB #$03 ; mask two low bits for enc. 0 JSR encoder_doit LDX #scounter1 LDAB EXPANSION_INPUTS ANDB #%00001100 LSRB LSRB JSR encoder_doit shaft_exit #endif //* re-enable interrupts PULA STAA TMSK1 RTI ; old CCR will be popped with interrupts //* globally enabled //* LCD clear screen operations LCD_clsops #if LCD_ROWS == 1 FDB $0001 ; home and clear screen FDB $0006 ; set cursor increment, no shift FCB $00 FCB $7F+LCD_COLS ; set cursor to frob char loc'n FDB $0200 ; print frobby char FDB $0002 ; return cursor home #endif #if LCD_ROWS == 2 FDB $0001 ; home and clear screen FDB $0006 ; set cursor increment, no shift FCB $00 FCB $BF+LCD_COLS ; set cursor to frob char loc'n;' FDB $0200 ; print frobby char FDB $0002 ; return cursor home #endif //* speedtable FCB %00000000 ; speed 0 FCB %00010001 FCB %01001001 FCB %01010101 FCB %01010111 FCB %01110111 FCB %01111111 FCB %11111111 ; speed 7 //* translates from ASCII 32 to ASCII 127 LCD_translation_table #ifdef LM576 //* for weird 15-char display FCB ' , '!, '", '#, '$, '%, '&, '' FCB '(, '), '*, '+, 2, '-, '., '/ FCB '0, '1, '2, '3, '4, '5, '6, 7 FCB '8, '9, 4, 5, '<, '=, '>, '? FCB '@, 'A, 'B, 'C, 'D, 'E, 'F, 'G FCB 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O FCB 'P, 'Q, 'R, 'S, 'T, 'U, 'V, 'W FCB 'X, 'Y, 'Z, '[, '\, '], '^, '_ FCB '`, 'a, 'b, 'c, 'd, 'e, 'f, 3 FCB 'h, 'i, 6, 'k, 'l, 'm, 'n, 'o FCB 0xe0, 0xe1, 'r, 's, 't, 'u, 'v, 'w FCB 'x, 1, 'z, '{, '|, '}, '~, 0x7F #endif #if defined (LCD_ROWS) && ! defined (LM576) //* for normal displays FCB ' , '!, '", '#, '$, '%, '&, '';' FCB '(, '), '*, '+, 2, '-, '., '/;' FCB '0, '1, '2, '3, '4, '5, '6, '7 FCB '8, '9, 4, 5, '<, '=, '>, '? FCB '@, 'A, 'B, 'C, 'D, 'E, 'F, 'G FCB 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O FCB 'P, 'Q, 'R, 'S, 'T, 'U, 'V, 'W FCB 'X, 'Y, 'Z, '[, '\, '], '^, '_ FCB '`, 'a, 'b, 'c, 'd, 'e, 'f, 3;' FCB 'h, 'i, 6, 'k, 'l, 'm, 'n, 'o;' FCB 0xf0, 0xf1, 'r, 's, 't, 'u, 'v, 'w FCB 'x, 1, 'z, '{, '|, '}, '~, 0x7F #endif #ifdef SBOT //* //* //* IR phase locked loop code //* //* algorithm: compare incoming waveform to a "waveform" generated //* internally. sum the # of errors (when the two waves don't match) //* over the course of one waveform. If the error count is large, //* then we have not "found" the desired frequency. If the error count //* is small, then we have. //* //* at rising edge of internal wave, make phase corrections by jumping //* ahead one notch or waiting one notch. //* //* incoming wave: //* //* + +-- -- -- --+ +-- -- -- --+ //* | | | | | //* +-- -- -- --+ +-- -- -- --+ +-- //* //* internal wave: (for 125 Hz detection) //* //* +-- -- -- --+ +-- -- -- --+ //* | | | | //* -- -- -- --+ +-- -- -- --+ +-- //* 0 1 2 3 4 6 7 8 0 1 2 3 //* 5 (state 4 after wait) //* //* at state 3, if incoming wave is high, we are behind it. //* jump to state 6 (catch up). //* at state 4, if incoming wave is low, we are ahead of it. //* go to state 5 (wait for it to catch up). //* all other states: proceed to next state. //* all states: accumulate error if internal wave != incoming wave. //* //* internal wave for 100 Hz detection //* //* +-- -- -- -- --+ //* | | //* -- -- -- -- --+ +-- -- -- -- -- //* 0 1 2 3 4 5 7 8 9 10 //* 6 (state 5 after wait) //* //* //* register usage: //* A==0 if sensor was 0, not zero otherwise //* X has ptr to table of IRdetect, IRphase, and IRerror //* Y has ptr to jumptable for either 100Hz or 125Hz detection //* (destroyed on exit) //* IR_SENSOR0 EQU %00100000 ; port D bit 5 IR_SENSOR1 EQU %00010000 ; bit 4 IR_SENSOR2 EQU %00001000 ; bit 3 IR_DETECT EQU 0 IR_PHASE EQU 3 IR_ERROR EQU 6 IR_DETECT_LIMIT EQU 2 ; max 2 errors per cycle for detect IR_DETECT_THRESHOLD EQU 5 ; # of detects for LCD status indicator IR_doitbaby: //* check if phase is > 10; if so, reset to zero LDAB IR_PHASE,X CMPB #10 BLS IR_phaseok CLRB STAB IR_PHASE,X IR_phaseok: //* multiply phase by 2 and calc jump ASLB ABY LDY 0,Y ; fetch jump JMP 0,Y IR_jumptable100 FDB IR_start_cycle ; phase 0 FDB IR_low_phase ; phase 1 FDB IR_low_phase ; phase 2 FDB IR_low_phase ; phase 3 FDB IR_pre_edge ; phase 4 FDB IR_post_edge ; phase 5 FDB IR_high_phase ; phase 6 FDB IR_high_phase ; phase 7 FDB IR_high_phase ; phase 8 FDB IR_high_phase ; phase 9 FDB IR_end_cycle ; phase 10 IR_jumptable125 FDB IR_start_cycle ; phase 0 FDB IR_low_phase ; phase 1 FDB IR_low_phase ; phase 2 FDB IR_pre_edge ; phase 3 FDB IR_post_edge ; phase 4 FDB IR_high_phase ; phase 5 FDB IR_high_phase ; phase 6 FDB IR_high_phase ; phase 7 FDB IR_end_cycle ; phase 8 //* the following should get called only when user switches from 125hz to //* 100hz detection on the fly FDB IR_start_cycle ; phase 0 FDB IR_start_cycle ; phase 0 //* start cycle calc IRdetect based on previous error; set new error //* based on current sensor val. IR_start_cycle: LDAB IR_ERROR,X CMPB #IR_DETECT_LIMIT BLS IR_detect_true //* no detect CLR IR_DETECT,X BRA IR_check_phase0 IR_detect_true //* increment byte until it reaches 0xFF LDAB IR_DETECT,X INCB BNE IR_detect_ok DECB IR_detect_ok STAB IR_DETECT,X IR_check_phase0 TSTA BEQ IR_phase0_noerr //* error LDAB #1 STAB IR_ERROR,X BRA IR_setphase1 IR_phase0_noerr CLR IR_ERROR,X LDAB #1 IR_setphase1 STAB IR_PHASE,X RTS //* IR_low_phase increment error count if incoming wave is high //* increment phase IR_low_phase: INC IR_PHASE,X TSTA BNE IR_incerrorreturn RTS IR_incerrorreturn INC IR_ERROR,X RTS //* IR_pre_edge if wave is high, inc error and goto phase n+3; //* otherwise, inc phase & return IR_pre_edge: TSTA BNE IR_catchup //* no prob; inc phase & return IR_incphasereturn INC IR_PHASE,X RTS //* inc error, goto phase n+3 IR_catchup: INC IR_ERROR,X LDAB IR_PHASE,X ADDB #3 STAB IR_PHASE,X RTS //* IR_post_edge if wave is low; inc error and goto phase n+1; //* otherwise, proceed normally to phase n+2 and return IR_post_edge: TSTA BEQ IR_wait //* normal exit: phase += 2 LDAB IR_PHASE,X ADDB #2 STAB IR_PHASE,X RTS //* inc error, inc phase IR_wait INC IR_ERROR,X INC IR_PHASE,X RTS //* IR_high_phase inc phase //* if wave is high; return; otherwise inc error & return IR_high_phase: INC IR_PHASE,X TSTA BEQ IR_incerrorreturn RTS //* IR_end_cycle set phase to zero //* if wave is low incr. error IR_end_cycle: CLR IR_PHASE,X TSTA BEQ IR_incerrorreturn RTS //* //* //* encoder: there are four possible states; //* between consecutive readings, encoder is allowed //* only transitions between adjacent states //* //* //* Signal 0 +-- -- -- --+ +-- -- -- -- //* | | | //* -- -- -- --+ +-- -- -- --+ //* //* Signal 1 -- --+ +-- -- -- --+ +-- -- //* | | | | //* +-- -- -- --+ +-- -- -- --+ //* sig0: 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 //* sig1: 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 //* //* //* COUNTER TRANSITION TABLE //* //* prev state //* 00 01 10 11 //* //* new 00 NOP DOWN UP ERR //* state //* 01 UP NOP ERR DOWN //* //* 10 DOWN ERR NOP UP //* //* 11 ERR UP DOWN NOP //* //* //* register usage: //* B = data from sensor (shifted into bits 0 and 1) //* X = encoder data in page 0 //* Y = destroyed //* SE_COUNTS EQU 0 SE_STATE EQU 2 encoder_doit: //* form jumptable byte based on prev. state in low two bits TBA ; save new state in A ASLB ASLB ; shift new state up by 2 ORAB SE_STATE,X ; OR in old state STAA SE_STATE,X ; store new state ASLB ; shift again for jumptable LDY #encoder_jumptable ABY LDY 0,Y JMP 0,Y encoder_up: LDD SE_COUNTS,X ADDD #1 STD SE_COUNTS,X encoder_nop: encoder_error: RTS encoder_down: LDD SE_COUNTS,X SUBD #1 STD SE_COUNTS,X RTS encoder_jumptable: FDB encoder_nop FDB encoder_down FDB encoder_up FDB encoder_error FDB encoder_up FDB encoder_nop FDB encoder_error FDB encoder_down FDB encoder_down FDB encoder_error FDB encoder_nop FDB encoder_up FDB encoder_error FDB encoder_up FDB encoder_down FDB encoder_nop #endif //***************************************************************** //* //* StopInt power down STOP instruction //* //* performs checksum of memory, then check //* that power is really gone //* //* if power is back, require that it stay //* on for .05 sec before believing that it //* is truly back //* //* also prints "*halted*" on LCD and //* turns off wired-or mode for TxD //* StopInt: #ifdef DEBUG BRA boarddiesnomsg #endif //* delay a little while, hoping for board power to return #ifdef SBOT BRA boarddiesnomsg #endif LDY #1000 si_waitloop DEY BNE si_waitloop //* test if power has returned; no = die LDX #$1000 BRCLR PORTA,X %00000100 boarddiesnomsg //* require that power stay on for 50 msec before returning LDD #7142 ; 100,000 clocks = 0.05 sec si_loop2 BRCLR PORTA,X %00000100 boarddiesnomsg ; [7 cycles] SUBD #1 ; [4 cycles] BNE si_loop2 ; [3 cycles] BRA boardlives boarddies: //* save D reg (has checksum) PSHB PSHA //* keep LCD routine from generating more interrupts BSR LCD_noint //* home LCD cursor LDD #$0002 JSR LCDstart //* print "*Halted*" on LCD LDX #haltmsg LDAA #2 si_loop LDAB 0,X BEQ si_loopdone JSR LCDstart INX BRA si_loop si_loopdone PULA JSR Byte2Hex2LCD PULA JSR Byte2Hex2LCD LDAA #2 LDAB #')' JSR LCDstart boarddiesnomsg: //* turn on wired-or mode (stop driving TxD LED) LDX #$1000 BSET SPCR,X #PORTD_WOM #ifdef SBOT //* turn on IR reflectance lamp BSET PORTA,X $20 #endif //* setup for STOP on IC1 interrupt CLRA TAP STOP haltmsg FCC '*Halted* (' FCB 0 //******************************************************************* //* Y contains stack pointer boardlives BSR LCD_noint //* notify user that power failure has occured LDX #powerlostmessage JSR printstringLCD LDX #$1000 BCLR TFLG1,X %11111011 ; clear interrupt flag BSR LCD_yesint RTI powerlostmessage FCC '-POWER GLITCH- ' FCB 0 //**************************************** LCD_noint LDAA #$01 ; NOP STAA LCDdriverCLI-LCDdriver+LCDstart RTS LCD_yesint LDAA #$0E ; CLI STAA LCDdriverCLI-LCDdriver+LCDstart RTS //************************************************************ //* performs checksum of memory //* returns result in D //* call with SP in zero page //* //* stop at location THE_ZERO_ARRAY (checksum word is stored there) memorysum LDX #$8000 CLRA CLRB memsumloop ADDD 0,X XGDX ADDD #2 XGDX CPX #THE_ZERO_ARRAY BNE memsumloop RTS //* bad interrupt? return! BadInt: RTI #define RESERVED_INT BadInt ;$FFC0-FFD4: Reserved #define SCI_INT BadInt ;$FFD6: SCI Serial System #define SPI_INT BadInt ;$FFD8: SPI Serial Transfer Complete #define PULSE_EDGE_INT BadInt ;$FFDA: Pulse Accumulator Input Edge #define PULSE_OVERFLOW_INT BadInt ;$FFDC: Pulse Accumulator Overflow #define TIMER_OVERFLOW_INT BadInt ;$FFDE: Timer Overflow //* #ifndef NO_BEEPER #define TIC4_TOC5_INT BeepInt ;$FFE0: Timer Input Capture 4/Output Compare 5 (TI4O5) /* #else //* #define TIC4_TOC5_INT BadInt ;$FFE0: Timer Input Capture 4/Output Compare 5 (TI4O5) //* #endif */ #define TOC4_INT SystemInt ;$FFE2: Timer Output Compare 4 (TOC4) #define TOC3_INT BadInt ;$FFE4: Timer Output Compare 3 (TOC3) #define TOC2_INT BadInt ;$FFE6: Timer Output Compare 2 (TOC2) #ifdef SBOT #define TOC1_INT IRInt ;$FFE8: Timer Output Compare 1 (TOC1) #else #define TOC1_INT BadInt ;$FFE8: Timer Output Compare 1 (TOC1) #endif #define TIC3_INT BadInt ;$FFEA: Timer Input Capture 3 (TIC3) #define TIC2_INT BadInt ;$FFEC: Timer Input Capture 2 (TIC2) #define TIC1_INT StopInt ;$FFEE: Timer Input Capture 1 (TIC1) #define RTI_INT BadInt ;$FFF0: Real Time Interrupt (RTI) #define IRQ_INT BadInt ;$FFF2: (External Pin or Parallel I/O) (IRQ) #define XIRQ_INT BadInt ;$FFF4: (Pseudo Non-Maskable Interrupt) (XIRQ) #define SWI_INT BadInt ;$FFF6: Software Interrupt (SWI) #define ILLEGAL_OP_INT BadInt ;$FFF8: Illegal Opcode Trap () #define COP_INT BadInt ;$FFFA: COP Failure (Reset) () #define CLOCK_MON_INT BadInt ;$FFFC: COP Clock Monitor Fail (Reset) () #define RESET_INT startup ;$FFFE: /RESET //****************************************************************************** //* //* DO NOT CHANGE THIS!! Instead, change the above defines. //* //* ORG $BFC0 FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB SCI_INT FDB SPI_INT FDB PULSE_EDGE_INT FDB PULSE_OVERFLOW_INT FDB TIMER_OVERFLOW_INT FDB TIC4_TOC5_INT FDB TOC4_INT FDB TOC3_INT FDB TOC2_INT FDB TOC1_INT FDB TIC3_INT FDB TIC2_INT FDB TIC1_INT FDB RTI_INT FDB IRQ_INT FDB XIRQ_INT FDB SWI_INT FDB ILLEGAL_OP_INT FDB COP_INT FDB CLOCK_MON_INT FDB RESET_INT //* //* DO NOT CHANGE THIS!! //* ORG $FFC0 FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB RESERVED_INT FDB SCI_INT FDB SPI_INT FDB PULSE_EDGE_INT FDB PULSE_OVERFLOW_INT FDB TIMER_OVERFLOW_INT FDB TIC4_TOC5_INT FDB TOC4_INT FDB TOC3_INT FDB TOC2_INT FDB TOC1_INT FDB TIC3_INT FDB TIC2_INT FDB TIC1_INT FDB RTI_INT FDB IRQ_INT FDB XIRQ_INT FDB SWI_INT FDB ILLEGAL_OP_INT FDB COP_INT FDB CLOCK_MON_INT FDB RESET_INT //* //* DO NOT CHANGE THIS!! //* //****************************************************************************** ORG THE_ZERO_ARRAY FCB 0 FCB 0 ORG VERSION_NUMBER //* current version is 2.81, eh? FCB 2 FCB 81