!!! 6502 Forth like tiny OS {{{ ; ; LCD.AS ; ; Implements a "FORTH-like" operating system on a small 6502 SBC. ; The hardware includes a 6502 running at 1 Mhz, a 6522, 2 KB of RAM, ; 10 KB of EEPROM, a 4-line by 16-char LCD display, and a 22-key keyboard. ; The keyboard is scanned via the 6522 ports. Keys include hex chars 0 thru ; F, Space, <CR>, and <BS>. The keyboard is scanned via polling, whenever no ; other code is executing. ; The LCD display is mapped into memory. ; The SBC has other hardware features that are not currently used. ; ; All keystrokes are in hex. The OS identifies the purpose of a keyword ; based on it's length: ; 1-char symbols are simple functions (currently Examine and Deposit) ; 2-char symbols are byte values ; 3-char symbols are FORTH-like 'words' ; 4-char symbols are word values. ; Example: A3C2 5B F00 ; The above line would do the following: push the word $A3C2 onto the stack, ; push the byte $5B onto the stack, then execute word 'F00'. ; ; 'Word' definitions are stored in an 8K EEPROM at $C000. The storage is ; divided into 32-byte blocks - all words start on a 32-byte boundary. ; The first byte of a block indicates the contents of the block: ; 0 - deleted or unused block ; 1 - block contains space-delimited tokens ; 2 - block contains executable code, ending with RTS ; The 2nd thru 4th bytes of a block contain the word's name, in ASCII. ; The remainder of the block contains either tokens or code. ; ; A number of words are defined so far: ; E examine memory (addr on top of stack) ; D deposit to memory (addr and data on stack) ; CCC compile a new word into EEPROM (finds an empty block) ; 001 POP - pop a byte from the stack ; 002 INCW - increment a word on the top of the stack ; 003 DUPW - duplicate a word ; 004 READ - read an addr - pop/read the addr, push the result byte ; 005 PRINTBNS - print a byte value ; 006 PRINTB - print a byte value plus a trailing space ; 007 PRINTW - print a word value plus a trailing space ; 008 DELAY - a short time delay - param is a byte ; 009 LDELAY - time delay, 1-second increments ; 00A WRITE - write a location - addr word and data byte are on stack ; 00B ADD - add 2 bytes on the stack, push the result ; 00C SUB - subtract 2 bytes ; 00D SHIFT-R - right-shift a byte ; 00E SHIFT-L - left-shift a byte ; 00F INC - increment a byte ; 010 DEC - decrement a byte ; 011 DO - the 'do' of a do-while loop ; 012 WHILE - the 'while' of a do-while loop ; ; The 'E' word examines/displays a memory location, but formats the ; output to be a subsequent 'D' command. For example, you enter: ; 0400 E ; then the next display line becomes: ; 0400 12 D ; where 12 is the current contents of address 0400. The cursor is ; sitting on the '1' in '12', so you can enter a new value easily ; if desired (just press Enter and the value is unchanged). ; After entering a new value, press Enter, and the display is now: ; 0401 AB D ; - note that the address incremented, and 'AB' is the current contents. ; ; The compile (CCC) word creates a new token-style word definition in ; the first available unused block (code-style words must be entered ; manually, using the 'D' command). ; Example: CCC BAA 89 005 ; This example compiles a new word, named 'BAA'. The function of BAA ; is to push $89 on the stack, then print that value. ; ; RAM is 0000-0800 ; 6522 I/O is at "VIA" (2000-200F) ; CA1 is for falling-edge priority interrupts ; CA2 ; CB2 is SR out ; CB1 is SR clock out ; PA0-2 are inputs for keyboard scan ; PA3 ; PA4-6 is interrupt priority ; PA7 ; PB0-7 are outputs for keyboard scan OUTBUF EQU $0040 ; transmit buffer INBUFF EQU $0080 ; receiver buffer ZPIND EQU $00FC ; for indirect LDA/STA DSTACK EQU $0700 ; bottom of Data Stack VIA EQU $2000 ; address of 6522 VIA ACIA EQU $4000 ; address of 6551 ACIA LCD0 EQU $6000 ; LCD display LCD1 EQU $6001 EE8K EQU $C000 ; FORTH 'word' storage, 8KB EEPROM EQU $F800 ; boot and kernel code, 2KB ; ; 6522 VIA definitions ; ORG VIA ORB .DS 1 ;Output Register B IRB EQU ORB ;Input Register B ORA .DS 1 ;Output Register A IRA EQU ORA ;Input Register A DDRB .DS 1 ;Data Direction Register B DDRA .DS 1 ;Data Direction Register A T1CL .DS 1 ;read: T1 counter, low-order ;write: T1 latches, low-order T1CH .DS 1 ;T1 counter, high-order T1LL .DS 1 ;T1 latches, low-order T1LH .DS 1 ;T1 latches, high-order T2CL .DS 1 ;read: T2 counter, low-order ;write: T2 latches, low-order T2CH .DS 1 ;T2 counter, high-order SR .DS 1 ;Shift Register ACR .DS 1 ;Auxiliary Control Register PCR .DS 1 ;Peripheral Control Register IFR .DS 1 ;Interrupt Flag Register IER .DS 1 ;Interrupt Enable Register ; ; 6551 ACIA definitions ; ORG ACIA ATXM .DS 1 ;Transmitter Register (write only) ARCX EQU ATXM ;Receiver Register (read only) ASTS .DS 1 ;Status Register (read only) ARES EQU ASTS ;Soft Reset (write only) ACMD .DS 1 ;Command Register ACTL .DS 1 ;Control Register ; ; page zero variable declarations ; ORG $0000 KBUFF .DS 32 ; keyboard input buffer ORACOP .DS 1 ; copy of 6522 ORA IFRCOP .DS 1 ; copy of 6522 IFR DLYCNT .DS 1 ; delay counter OUTNDX .DS 1 ; output char index UASTAT .DS 1 ; UART status FDA .DS 1 CURSOR .DS 1 ; LCD cursor location TEMP .DS 1 FRADLO .DS 1 FRADHI .DS 1 FCNT .DS 1 FFDA .DS 1 FRENLO .DS 1 FRENHI .DS 1 ECNTLO .DS 1 ECNTHI .DS 1 TOADLO .DS 1 TOADHI .DS 1 KEY .DS 1 TOKEN1 .DS 1 ; ptr: beginning of token TOKEN2 .DS 1 ; ptr: end of token DSP .DS 1 ; data stack pointer ASCBYT .DS 2 TKLEN .DS 1 ; token length EEPTR .DS 2 ; WLETT .DS 1 ; BTYPE .DS 1 ; type: tokens, code, deleted BUFFPTR .DS 2 ; ptr to current token buffer TKTMP .DS 1 COMPTR .DS 2 ; ptr used by Compile ORG INBUFF INNDX .DS 1 ; input buffer index ORG EEPROM ; $F800 LCDBUSY: ; wait for the LCD to not be busy PHA L1: LDA LCD0 AND #$80 BNE L1 PLA RTS LCDINIT: ; init the LCD display LDX #$04 ; do it 4 times L0: LDA #$38 ; STA LCD0 JSR LCDBUSY DEX BNE L0 LDA #$06 ; STA LCD0 JSR LCDBUSY LDA #$0E ; STA LCD0 JSR LCDBUSY LDA #$01 ; clear display STA LCD0 JSR LCDBUSY LDA #$90 ; start on 3rd line STA CURSOR ; init cursor location STA LCD0 JSR LCDBUSY RTS ORG $F850 SCROLL: ; Treat the 4 X 16 display as 2 lines of 32 char each. ; scroll everything up one line ; leave cursor at start of 2nd line (3rd physical line) LDX #$00 ; index, beginning of kybd buffer LDA #$90 ; 3rd line S6: STA LCD0 ; set cursor pos JSR LCDBUSY S1: LDA LCD1 ; get char from display STA KBUFF,X ; copy char to buffer JSR LCDBUSY INX ; next char CPX #$20 ; all done? BEQ S2 ; yes CPX #$10 ; end of line? BNE S1 ; no LDA #$D0 ; yes, goto 4th display line BNE S6 S2: ; copy buffer to 1st line LDX #$00 ; beginning of buffer LDA #$80 ; 1st line S5: STA LCD0 ; set cursor pos JSR LCDBUSY S3: LDA KBUFF,X ; get char from buffer STA LCD1 ; write to display JSR LCDBUSY INX ; next char CPX #$20 ; all done? BEQ S4 ; yes CPX #$10 ; end of line? BNE S3 ; no LDA #$C0 ; yes, goto 2nd line BNE S5 S4: ; fill 3rd and 4th lines with spaces LDA #$90 ; goto 3rd line JSR BLNKLN LDA #$D0 ; goto 4th line JSR BLNKLN ; move cursor to start of 3rd physical line LDA #$90 STA CURSOR STA LCD0 JSR LCDBUSY RTS BLNKLN: ; fill a 16-char line with spaces STA LCD0 JSR LCDBUSY LDX #$10 ; line len is 16. BL1: LDA #$20 ; write a space STA LCD1 JSR LCDBUSY DEX BNE BL1 RTS ORG $F8E0 PRINTCH: ; Display a char at the current cursor location. ; 80...8F 1st line ; C0...CF 2nd line ; 90...9F 3rd line ; D0...DF 4th line CMP #$0D ; <CR> ? BNE P1 ; no JMP SCROLL ; yes, just scroll and return P1: CMP #$08 ; backspace ? BNE P2 ; no JMP BACKSPACE ; yes, just BS and return P2: STA LCD1 ; display it JSR LCDBUSY ; decide where the next char should go LDX CURSOR ; get cursor location INX ; move cursor forward CPX #$E0 ; end of 4th line? BNE P4 ; no DEX ; yes, stay where we were P4: CPX #$A0 ; end of 3rd line? BNE P3 ; no LDX #$D0 ; yes, begin 4th line P3: STX CURSOR ; remember new location STX LCD0 ; set the new location JSR LCDBUSY RTS ORG $F90F BACKSPACE: ; backup the cursor one space LDX CURSOR CPX #$D0 ; beginning of 4th line? BNE BS1 ; no LDX #$A0 ; yes, goto end of 3rd line BS1: CPX #$90 ; beginning of 3rd line? BEQ BS2 ; yes, do nothing DEX ; no, backup one space NOP BS2: STX CURSOR ; remember new location STX LCD0 ; set new location JSR LCDBUSY LDA #$20 ; print a space to overwrite old char STA LCD1 JSR LCDBUSY LDA CURSOR ; put cursor back on the space STA LCD0 JSR LCDBUSY RTS ORG $F950 DOKEY: ; handle an input char PHA ; save A JSR PRINTCH ; print it PLA ; restore A CMP #$0D ; <CR> ? BNE D1 ; no, we're done JSR PARSEK ; yes, parse the input line D1: RTS KBDINIT: LDA #$82 ; enable CA1 ints from 6522 ; STA IER NOP NOP NOP LDA #$00 STA DDRA ; port A is input STA ORB ; all 0's on output port STA INNDX ; init inbuff index LDA #$FF STA DDRB ; port B is output RTS CHKKBD: ; get the key (if pressed) ; the key is returned in A ; if no key pressed, return 00 LDY #$FE ; start with 11111110 C1: STY ORB ; scan 1 of 8 rows LDA IRA ; read the column AND #$07 ; lower 3 bits only CMP #$07 ; key ? BNE C2 ; yes, go decode it TYA ; no BMI C3 ; is it 01111111 yet? LDA #$00 ; yes, no key was pressed RTS ; quit - return 00 C3: SEC ; no ROL A ; shift left TAY ; put the pattern back in Y CLC ; go scan the next row BCC C1 C2: STA KEY ; save the column info TYA ; get the row LDY #$00 ; init counter C4: INY ; count LSR A ; shift the row BCS C4 ; until the 0 falls out TYA ; get the row-count ASL A ; move it to the higher bits ASL A ASL A ORA KEY ; combine row and column ; decode the key value into ASCII LDX #$17 C6: CMP KEYTAB,X BEQ C5 DEX BPL C6 LDA #$00 BEQ C7 C5: LDA ASCTAB,X C7: STA KEY ; save the ASCII key LDA #$14 ; wait for debounce JSR DELAY JSR WAITNOKEY ; wait for no key LDA #$30 ; wait for debounce JSR DELAY JSR WAITNOKEY ; wait for no key LDA KEY ; get the ASCII key char RTS ; return the char WAITNOKEY: ; wait for no key being pressed LDA #$00 STA ORB W1: LDA IRA AND #$07 CMP #$07 BNE W1 RTS KEYTAB: ; keyboard codes .BYTE $0D .BYTE $3E .BYTE $3D .BYTE $26 .BYTE $25 .BYTE $36 .BYTE $35 .BYTE $2E .BYTE $2D .BYTE $46 .BYTE $45 .BYTE $16 .BYTE $15 .BYTE $1E .BYTE $1D .BYTE $0E .BYTE $23 .BYTE $13 .BYTE $2B .BYTE $0B .BYTE $33 .BYTE $1B .BYTE $00 ; not used .BYTE $00 ; not used ASCTAB: ; ASCII codes .BYTE $30 ; 0 .BYTE $31 .BYTE $32 .BYTE $33 .BYTE $34 .BYTE $35 .BYTE $36 .BYTE $37 .BYTE $38 .BYTE $39 ; 9 .BYTE $41 ; A .BYTE $42 .BYTE $43 .BYTE $44 .BYTE $45 .BYTE $46 ; F .BYTE $00 .BYTE $00 .BYTE $00 .BYTE $20 ; space .BYTE $08 ; backspace .BYTE $0D ; <CR> .BYTE $00 ; not used .BYTE $00 ; not used ORG $FA30 PARSEK: ; Check the input line, see what to do. ; Process each token found on the line. LDA #$00 ; beginning of input line STA BUFFPTR ; point to kybd buffer STA BUFFPTR+1 STA TOKEN1 PK1: JSR GETTOKEN ; get the next token JSR DOTOKEN ; process it BNE PK1 ; more tokens? RTS NOP NOP NOP NOP NOP NOP GETTOKEN: ; identify the next token in the current buffer ; BUFFPTR points to the buffer ; max buffer length is 32. ; TOKEN1 points to first char in token. ; TOKEN2 points to first space after token. LDY TOKEN1 ; start from here GTLOOP: LDA (BUFFPTR),Y ; get a char CMP #$00 ; end of buffer marker? BEQ G3 ; yes - quit CMP #$20 ; space? BNE SYM ; no - it's a token INY ; yes - skip it CPY #$20 ; end of buffer? BNE GTLOOP ; no, loop G3: LDA #$00 ; yes, report no more tokens STA TOKEN2 GTDONE: RTS ; return SYM: STY TOKEN1 ; point to beginning of token GTL2: INY ; next char CPY #$20 ; end of buffer? BNE G1 ; no G2: STY TOKEN2 ; yes, point to end of token RTS G1: LDA (BUFFPTR),Y ; get next char CMP #$20 ; space? BNE GTL2 ; no, loop BEQ G2 ; yes, quit NOP NOP NOP NOP NOP NOP DOTOKEN: ; Process a token from the current buffer. ; Process each token based on it's length. ; Len of 1 is a 'kernel' command. ; Len of 3 is a command word. ; Len of 2 is a byte value. ; Len of 4 is a word value. LDA TOKEN2 ; was a token found? BEQ DT1 ; no, quit SEC ; yes, determine it's length SBC TOKEN1 ; subtract STA TKLEN ; save the length CMP #$01 BNE DT2 JSR ONE CLC BCC DT1 DT2: LDA TKLEN CMP #$02 BNE DT3 JSR TWO CLC BCC DT1 DT3: LDA TKLEN CMP #$03 BNE DT4 JSR THREE CLC BCC DT1 DT4: LDA TKLEN CMP #$04 BNE DT5 JSR FOUR CLC BCC DT1 DT5: LDA #$00 ; unsupported token length STA TOKEN2 DT1: LDA TOKEN2 STA TOKEN1 RTS ONE: ; Is it a 'D'? LDY TOKEN1 LDA (BUFFPTR),Y ; get the letter CMP #'D BEQ DEPOSIT CMP #'E BEQ EXAMINE RTS TWO: ; push a byte onto the dstack ; convert ASCII byte to binary LDY TOKEN1 ; point to hi-order char JSR CVTBYT JMP PUSH ; push onto dstack THREE: ; It's a command 'word'. Execute it. JMP DOWORD FOUR: ; Push a word onto the dstack. Do it hi-byte first, ; so that it can be used as a pointer directly. LDY TOKEN1 JSR CVTBYT NOP NOP NOP NOP JSR PUSH NOP NOP JSR CVTBYT JMP PUSH ; push lo byte DEPOSIT: JSR POP JSR DUPW JSR WRITEA LDA #$01 JSR DELAY JSR INCW EXAMINE: JSR DUPW JSR PRINTW JSR READ JSR PRINTB LDA #'D JSR PRINTCH ; backup cursor 4 spaces LDX CURSOR DEX DEX DEX DEX STX CURSOR ; remember new location STX LCD0 ; set new location JSR LCDBUSY RTS CVTBYT: ; Get an ASCII byte from the buffer, convert to binary. LDA (BUFFPTR),Y JSR BINBYT ; convert to binary ASL A ; move to upper nibble ASL A ASL A ASL A STA TEMP ; save it INY ; get lo-order char LDA (BUFFPTR),Y JSR BINBYT ; convert to binary CLC ; add upper nibble ADC TEMP INY ; point to next char RTS NOP NOP NOP NOP NOP NOP ORG $FB80 PUSH: ; push A onto the dstack LDX DSP STA DSTACK,X DEC DSP RTS POP: ; pop A from the dstack INC DSP LDX DSP LDA DSTACK,X RTS INCW: ; Increment the word on the top of the stack, ; but don't pop it. LDX DSP INX INC DSTACK,X BNE IW1 INX INC DSTACK,X IW1: RTS WRITEA: ; Write A to the location indicated by the word ; on the top of the stack. ; Preserve A, pop the address. PHA LDA #$8D STA ZPIND JSR POP STA ZPIND+1 JSR POP STA ZPIND+2 LDA #$60 STA ZPIND+3 PLA JSR ZPIND RTS DUPW: ; Push a duplicate of the word that is already ; on the top of the stack. ; Preserve A. PHA LDY DSP INY INY LDA DSTACK,Y JSR PUSH DEY LDA DSTACK,Y JSR PUSH PLA RTS READ: ; Read a memory location, specified by the addr ; on the stack. Pop the address, and push the ; result. Also return the result in A. LDA #$AD STA ZPIND JSR POP STA ZPIND+1 JSR POP STA ZPIND+2 LDA #$60 STA ZPIND+3 JSR ZPIND JSR PUSH RTS PRINTBNS: ; Print a byte value in hex at the ; current cursor location. Do NOT include a space. JSR POP JSR BYTEHEX LDA ASCBYT JSR PRINTCH LDA ASCBYT+1 JSR PRINTCH RTS PRINTB: ; Print a byte value in hex at the ; current cursor location. Include a space. JSR PRINTBNS LDA #$20 JSR PRINTCH RTS PRINTW: ; Print a word value in hex at the ; current cursor location. Include a space. JSR POP PHA JSR PRINTBNS PLA JSR PUSH JSR PRINTB RTS DOWORD: ; process a 3-letter word from the current buffer ; save the current BUFFPTR and TOKEN2 LDA BUFFPTR PHA LDA BUFFPTR+1 PHA LDA TOKEN2 PHA ; find the word's definition block LDA #$00 ; point to start of word defs STA EEPTR LDA #$C0 STA EEPTR+1 DW3: LDA TOKEN1 STA TKTMP LDY #$00 ; check block type LDA (EEPTR),Y STA BTYPE BNE DW1 ; deleted? DW2: LDA EEPTR ; yes - goto next block CLC ADC #$20 ; blk size is 32. STA EEPTR BCC DW5 INC EEPTR+1 LDA EEPTR+1 CMP #$D8 ; end of EE ? BEQ DWDONE DW5: CLC ; loop back BCC DW3 DW1: INY ; check the symbol LDA (EEPTR),Y STA WLETT TYA ; save Y TAX LDY TKTMP ; get token letter LDA (BUFFPTR),Y CMP WLETT ; letter match? BNE DW2 ; no - goto next block TXA ; restore Y TAY INC TKTMP ; yes - next letter CPY #$03 ; done? BNE DW1 ; no - next letter ; found matching word block! LDA BTYPE ; check blk type CMP #$01 ; tokens? BEQ DW7 ; yes CMP #$02 ; code? BEQ DW4 ; yes BNE DWDONE ; unknown - quit NOP NOP DW7: ; found token block - point to 1st token INY ; point to space before token INY ; point to first letter of token ; process tokens STY TOKEN1 STY TOKEN2 ; set the BUFFPTR to match EEPTR LDA EEPTR STA BUFFPTR LDA EEPTR+1 STA BUFFPTR+1 ; recurse DW6: JSR GETTOKEN ; get a token JSR DOTOKEN ; process it BNE DW6 ; more tokens? DWDONE: ; restore the BUFFPTR and TOKEN1 PLA STA TOKEN2 STA TOKEN1 PLA STA BUFFPTR+1 PLA STA BUFFPTR RTS DW4: ; process code LDA #$4C ; make a JMP in 0-page STA ZPIND LDA EEPTR ; point to the block CLC ADC #$04 ; the 4th byte is the start STA ZPIND+1 LDA EEPTR+1 STA ZPIND+2 JSR ZPIND ; make the call CLC ; done BCC DWDONE NOP NOP NOP NOP NOP NOP COMPILE: ; Compile a new word into EE. The definition is ; in (BUFFPTR)+TOKEN2 . ; find an unused or deleted block LDA #$00 ; EE starts at $C000 STA COMPTR LDA #$C0 STA COMPTR+1 COM1: LDY #$00 ; 1st byte of blk is type LDA (COMPTR),Y ; get block type CMP #$01 ; token block? BEQ USEDBL ; yes - find another CMP #$02 ; code block? BEQ USEDBL ; yes - find another ; this block is free! ; (BUFFPTR)+TOKEN2 points to space before 1st token LDA #$00 STA TKTMP ; (COMPTR)+TKTMP now points to block type LDA #$01 ; write the block type JSR EEWRT COM2: INC TKTMP ; next output index INC TOKEN2 ; next input index LDY TOKEN2 ; beyond end of input buffer? CPY #$20 BEQ DONULL ; yes LDA (BUFFPTR),Y ; get a char from buffer JSR EEWRT ; write the char CLC ; do next char BCC COM2 DONULL: LDA #$00 ; write a null terminator JSR EEWRT COMDONE: LDA #$00 ; don't process rest of buffer STA TOKEN2 RTS ; return USEDBL: LDA COMPTR ; goto next block CLC ADC #$20 ; blk size is 32. STA COMPTR BCC COM1 INC COMPTR+1 LDA COMPTR+1 CMP #$D8 ; limit of blocks ? BEQ COMDONE ; yes - quit BNE COM1 ; no - try this block EEWRT: LDY TKTMP ; get index STA (COMPTR),Y ; write the char LDA #$0D ; wait 50ms for EE to finish JSR DELAY RTS ORG $FDA0 BOOT: ; ; The Reset vector (FFFC) points here. ; SEI ; no IRQ interrupts CLD ; no Decimal mode JSR LCDINIT JSR KBDINIT LDA #$C0 STA ARES ; ACIA soft reset LDA #$1A ; 8-N-1, 2400 baud STA ACTL LDA #$FF STA DSP ; LDA #$89 ; enable ACIA xmtr/rcvr ; STA ACMD NOP NOP NOP NOP NOP CLI ; enable ints ; print "OK" LDA #$4F ; 'O' JSR PRINTCH LDA #$4B ; 'K' JSR PRINTCH LDA #$0D ; <CR> JSR PRINTCH ; Go into the idle loop. ; Check the kbd, about 60 times/second. ; Any other work must be interrupt-driven. IDLE: JSR CHKKBD BEQ IDLE ; no key JSR DOKEY ; handle the key LDA #$04 ; delay JSR DELAY CLC ; loop BCC IDLE ORG $FE00 BINBYT: ; Convert a single hex-ascii char to binary. CMP #$60 BMI BB2 AND #$DF BB2: SEC SBC #$30 CMP #$11 BMI BB1 SEC SBC #$07 BB1: RTS ; Convert the value in A into two hex-ascii bytes, and store ; the two bytes in ASCBYT. BYTEHEX: PHA ; save the byte LSR A ; do the high nibble first LSR A LSR A LSR A CLC ; make it ascii ADC #$30 CMP #$3A ; if it's a letter, add a little more BMI BH3 CLC ADC #$07 BH3: STA ASCBYT ; store the ascii byte PLA ; get the original byte AND #$0F ; do the low nibble CLC ; make it ascii ADC #$30 CMP #$3A BMI BH4 CLC ADC #$07 BH4: STA ASCBYT+1 ; store the 2nd ascii byte RTS ; Time delay. ; Put the delay value in A. A value of 00 is 256 loops (max delay). ; With 256 loops, the number of machine cycles is: 329479+131072N, ; where N is the number of NOP instructions. If N is 5, and the ; clock is 1 Mhz, then the max delay is 0.985 seconds, or 3.85 ms ; per loop. If N is 12, and the clock is 1.8432 Mhz, then the max ; delay is 1.032 seconds, or 4.03 ms per loop. ; X and Y are preserved, Status is not. ; Location DLYCNT is used for temp storage. DELAY: STA DLYCNT TXA ; save X PHA LDX #$0 ; init X DLOOP: INX ; waste time NOP NOP NOP NOP NOP BNE DLOOP ; did X wrap? DEC DLYCNT ; yes, decrement counter BNE DLOOP ; are we done? No - loop again. PLA ; we're done - restore X TAX RTS ; return ; Do longer delays, in multiples of about 1 second. ; Put the desired delay in A. ; A value of 00 will return immediately. LDELAY: BEQ LDELAY2 ; if 00, return right away TAX ; save the counter in X LDELAY1: LDA #$00 ; do a 1-second delay JSR DELAY DEX ; DEC the counter BNE LDELAY1 ; done? LDELAY2: RTS ; yes - return ; ; NMI entry point. ; NMI: RTI ; ; IRQ entry point. ; IRQ: RTI ; return from interrupt ; INTERRUPT AND RESTART VECTORS ORG $FFFA .WORD NMI ; NMI vector .WORD BOOT ; Cold start & reset vector .WORD IRQ ; IRQ vector (and BRK vector) .END BOOT }}}