diff --git a/examples/65Org16.boot.asm b/examples/65Org16.boot.asm new file mode 100644 index 0000000..769a3f1 --- /dev/null +++ b/examples/65Org16.boot.asm @@ -0,0 +1,235 @@ + .LIST + +; bootrom for py65 monitor in 65Org16 mode +; based on +; Intel Hex format loader by Ross Archer (9 February 2001, freeware) +; from http: http://www.6502.org/source/monitors/intelhex/intelhex.htm +; + +; use this monitor like this: +; PYTHONPATH=. python py65/monitor.py -m 65Org16 +; load bootrom.bin fe00 +; goto fe00 + +START = $FFFFFe00 + +; I/O is memory-mapped in py65: +PUTC = $f001 +GETC = $f005 ; blocking input + +; Note that Hex format for 65Org16 uses ';' not ':' as the start of record mark +; also note that some fields are now composed of 16-bit elements: +; previously: +; length offset type data checksum +; :/08/E008/00/08090A0B0C0D0E0F/xx +; now +; ;/10/E008/00/00080009000A000B000C000D000E000F/xx + +; Zero-page storage +DPL = $00 ; data pointer (two bytes) used by PUTSTRI +DPH = $01 ; high of data pointer +RECLEN = $02 ; record length in bytes +START_LO = $03 +START_HI = $04 +RECTYPE = $05 +CHKSUM = $06 ; record checksum accumulator +DLFAIL = $07 ; flag for download failure +TEMP = $08 ; save hex value +TMPHEX = $09 ; save another hex value + + ; where the RAM program MUST have its first instruction +ENTRY_POINT = $0200 + + .ORG START + + sei ; disable interrupts + cld ; binary mode arithmetic + ldx #$1FFFF ; Set up the stack pointer + txs ; " + + ; Download Intel hex. The program you download MUST have its entry + ; instruction (even if only a jump to somewhere else) at ENTRY_POINT. +HEXDNLD lda #0 + sta START_HI ; store all programs in bank 0 (page 0) for now + sta DLFAIL ;Start by assuming no D/L failure + jsr PUTSTRI + .byte 13,10,13,10 + .byte "Send 65Org16 code in" + .byte " variant Intel Hex format" + .byte " at 19200,n,8,1 ->" + .byte 13,10 + .byte 0 ; Null-terminate unless you prefer to crash. +HDWRECS jsr GETSER ; Wait for start of record mark ';' + cmp #';' + bne HDWRECS ; not found yet + ; Start of record marker has been found + lda #0 + sta CHKSUM + jsr GETHEX ; Get the record length + sta RECLEN ; save it + jsr GET4HX ; Get the 16-bit offset + sta START_LO + jsr GETHEX ; Get the record type + sta RECTYPE ; & save it + bne HDER1 ; end-of-record + ldx RECLEN ; number of data bytes to write to memory + ldy #0 ; start offset at 0 +HDLP1 jsr GET4HX ; Get the first/next/last data word + sta (START_LO),y ; Save it to RAM + iny ; update data pointer + dex ; decrement character count + dex ; ... twice + bne HDLP1 + jsr GETHEX ; get the checksum + lda CHKSUM + bne HDDLF1 ; If failed, report it + ; Another successful record has been processed + lda #'#' ; Character indicating record OK = '#' + sta PUTC ; write it out but don't wait for output + jmp HDWRECS ; get next record +HDDLF1 lda #'F' ; Character indicating record failure = 'F' + sta DLFAIL ; download failed if non-zero + sta PUTC ; write it to transmit buffer register + jmp HDWRECS ; wait for next record start +HDER1 cmp #1 ; Check for end-of-record type + beq HDER2 + jsr PUTSTRI ; Warn user of unknown record type + .byte 13,10,13,10 + .byte "Unknown record type $" + .byte 0 ; null-terminate unless you prefer to crash! + lda RECTYPE ; Get it + sta DLFAIL ; non-zero --> download has failed + jsr PUTHEX ; print it + lda #13 ; but we'll let it finish so as not to + jsr PUTSER ; falsely start a new d/l from existing + lda #10 ; file that may still be coming in for + jsr PUTSER ; quite some time yet. + jmp HDWRECS + ; We've reached the end-of-record record +HDER2 jsr GETHEX ; get the checksum + lda CHKSUM ; Add previous checksum accumulator value + beq HDER3 ; checksum = 0 means we're OK! + jsr PUTSTRI ; Warn user of bad checksum + .byte 13,10,13,10 + .byte "Bad record checksum!",13,10 + .byte 0 ; Null-terminate or 6502 go bye-bye + jmp START +HDER3 lda DLFAIL + beq HDEROK + ;A download failure has occurred + jsr PUTSTRI + .byte 13,10,13,10 + .byte "Download Failed",13,10 + .byte "Aborting!",13,10 + .byte 0 ; null-terminate every string yada yada. + jmp START +HDEROK jsr PUTSTRI + .byte 13,10,13,10 + .byte "Download Successful!",13,10 + .byte "Jumping to location $" + .byte 0 ; by now, I figure you know what this is for. :) + lda #HI(ENTRY_POINT) ; Print the entry point in hex + jsr PUTHEX + lda #LO(ENTRY_POINT) + jsr PUTHEX + jsr PUTSTRI + .byte 13,10 + .byte 0 ; stop lemming-like march of the program ctr. thru data + jmp ENTRY_POINT ; jump to canonical entry point + +; For py65, the input routine will block until a character arrives +GETSER lda GETC + rts + +; get four ascii chars, adding both octets into the checksum +GET4HX jsr GETHEX + asl a + asl a + asl a + asl a + asl a + asl a + asl a + asl a + sta TMPHEX + jsr GETHEX + ora TMPHEX + rts + +; get two ascii chars, add into the checksum +GETHEX jsr GETSER + jsr MKNIBL ; Convert to 0..F numeric + asl a + asl a + asl a + asl a ; This is the upper nibble + and #$F0 + sta TEMP + jsr GETSER + jsr MKNIBL + ora TEMP + sta TEMP + clc + adc CHKSUM ; Add in the checksum + and #$ff + sta CHKSUM ; + lda TEMP + rts ; return with the nibble received + +; Convert the ASCII nibble to numeric value from 0-F: +MKNIBL cmp #'9'+1 ; See if it's 0-9 or 'A'..'F' (no lowercase yet) + bcc MKNNH ; If we borrowed, we lost the carry so 0..9 + sbc #7+1 ; Subtract off extra 7 (sbc subtracts off one less) + ; If we fall through, carry is set unlike direct entry at MKNNH +MKNNH sbc #'0'-1 ; subtract off '0' (if carry clear coming in) + and #$0F ; no upper nibble no matter what + rts ; and return the nibble + +; Put byte in A as hexydecascii +PUTHEX pha ; + lsr a + lsr a + lsr a + lsr a + jsr PRNIBL + pla +PRNIBL and #$0F ; strip off the low nibble + cmp #$0A + bcc NOTHEX ; if it's 0-9, add '0' else also add 7 + adc #6 ; Add 7 (6+carry=1), result will be carry clear +NOTHEX adc #'0' ; If carry clear, we're 0-9 +; Write the character in A as ASCII: +PUTSER sta PUTC + rts + +;Put the string following in-line until a NULL out to the console +PUTSTRI pla ; Get the low part of "return" address (data start address) + sta DPL + pla + sta DPH ; Get the high part of "return" address + ; (data start address) + ; Note: actually we're pointing one short +PSINB ldy #1 + lda (DPL),y ; Get the next string character + inc DPL ; update the pointer + bne PSICHO ; if not, we're pointing to next character + inc DPH ; account for page crossing +PSICHO ora #0 ; Set flags according to contents of Accumulator + beq PSIX1 ; don't print the final NULL + jsr PUTSER ; write it out + jmp PSINB ; back around +PSIX1 inc DPL ; + bne PSIX2 ; + inc DPH ; account for page crossing +PSIX2 jmp (DPL) ; return to byte following final NULL +; +; Dummy interrupt handlers +GOIRQ +GONMI RTI + +; vectors + .ORG $FFFFFFFA +NMIENT .word GONMI +RSTENT .word START +IRQENT .word GOIRQ +.end ; finally. das Ende. diff --git a/examples/65Org16.boot.rom b/examples/65Org16.boot.rom new file mode 100644 index 0000000..96388c7 Binary files /dev/null and b/examples/65Org16.boot.rom differ diff --git a/examples/README.txt b/examples/README.txt new file mode 100644 index 0000000..c94d671 --- /dev/null +++ b/examples/README.txt @@ -0,0 +1,12 @@ +Some example code and usage: + +For linux: + +(no need to install or build - just unpack) + +$ cd src +$ env PYTHONPATH=. python py65/monitor.py -m 65Org16 -r ../examples/65Org16.boot.rom + +Then paste in a hex file, such as ../examples/swapcase.hex + +(Type in some mixed-case input and it will echo it back with the upper and lowercase swapped) diff --git a/examples/swapcase.asm b/examples/swapcase.asm new file mode 100644 index 0000000..eafe5da --- /dev/null +++ b/examples/swapcase.asm @@ -0,0 +1,28 @@ +; trivial demo program: read input, swap case and write out + + ; assemble using Anton Treuenfels' HXA assembler + ; with I6502.A macro file + ; HXA_TW.EXE swapcase.asm + ; (runs on Linux if you have WINE installed) + ; which will make + ; SWAPCASE.HEX + ; where the first line should not be input to the hexloader + + .hexfile + + .cpu T_32_M16 + .assume BIT32=1032, BIT32R=3210 + .include "i6502.a" + +; I/O is memory-mapped in py65: +PUTC = $f001 +GETC = $f005 ; blocking input + +; the py65 hexload boot ROM will only load to $0200 + .ORG $200 + +another + lda GETC + eor #$20 ; swap upper and lower case as a demo + sta PUTC + jmp another diff --git a/examples/swapcase.hex b/examples/swapcase.hex new file mode 100644 index 0000000..fcce8a4 --- /dev/null +++ b/examples/swapcase.hex @@ -0,0 +1,3 @@ +;020000040000FA +;1202000000A5F005004900200085F001004C0200000025 +;00000001FF