diff --git a/firmware1704/crc16hi.inc b/firmware1704/crc16hi.inc new file mode 100644 index 0000000..40b3b41 --- /dev/null +++ b/firmware1704/crc16hi.inc @@ -0,0 +1,257 @@ +;LUT for high byte of CCITT CRC16 + retlw 0x00 + retlw 0x10 + retlw 0x20 + retlw 0x30 + retlw 0x40 + retlw 0x50 + retlw 0x60 + retlw 0x70 + retlw 0x81 + retlw 0x91 + retlw 0xA1 + retlw 0xB1 + retlw 0xC1 + retlw 0xD1 + retlw 0xE1 + retlw 0xF1 + retlw 0x12 + retlw 0x02 + retlw 0x32 + retlw 0x22 + retlw 0x52 + retlw 0x42 + retlw 0x72 + retlw 0x62 + retlw 0x93 + retlw 0x83 + retlw 0xB3 + retlw 0xA3 + retlw 0xD3 + retlw 0xC3 + retlw 0xF3 + retlw 0xE3 + retlw 0x24 + retlw 0x34 + retlw 0x04 + retlw 0x14 + retlw 0x64 + retlw 0x74 + retlw 0x44 + retlw 0x54 + retlw 0xA5 + retlw 0xB5 + retlw 0x85 + retlw 0x95 + retlw 0xE5 + retlw 0xF5 + retlw 0xC5 + retlw 0xD5 + retlw 0x36 + retlw 0x26 + retlw 0x16 + retlw 0x06 + retlw 0x76 + retlw 0x66 + retlw 0x56 + retlw 0x46 + retlw 0xB7 + retlw 0xA7 + retlw 0x97 + retlw 0x87 + retlw 0xF7 + retlw 0xE7 + retlw 0xD7 + retlw 0xC7 + retlw 0x48 + retlw 0x58 + retlw 0x68 + retlw 0x78 + retlw 0x08 + retlw 0x18 + retlw 0x28 + retlw 0x38 + retlw 0xC9 + retlw 0xD9 + retlw 0xE9 + retlw 0xF9 + retlw 0x89 + retlw 0x99 + retlw 0xA9 + retlw 0xB9 + retlw 0x5A + retlw 0x4A + retlw 0x7A + retlw 0x6A + retlw 0x1A + retlw 0x0A + retlw 0x3A + retlw 0x2A + retlw 0xDB + retlw 0xCB + retlw 0xFB + retlw 0xEB + retlw 0x9B + retlw 0x8B + retlw 0xBB + retlw 0xAB + retlw 0x6C + retlw 0x7C + retlw 0x4C + retlw 0x5C + retlw 0x2C + retlw 0x3C + retlw 0x0C + retlw 0x1C + retlw 0xED + retlw 0xFD + retlw 0xCD + retlw 0xDD + retlw 0xAD + retlw 0xBD + retlw 0x8D + retlw 0x9D + retlw 0x7E + retlw 0x6E + retlw 0x5E + retlw 0x4E + retlw 0x3E + retlw 0x2E + retlw 0x1E + retlw 0x0E + retlw 0xFF + retlw 0xEF + retlw 0xDF + retlw 0xCF + retlw 0xBF + retlw 0xAF + retlw 0x9F + retlw 0x8F + retlw 0x91 + retlw 0x81 + retlw 0xB1 + retlw 0xA1 + retlw 0xD1 + retlw 0xC1 + retlw 0xF1 + retlw 0xE1 + retlw 0x10 + retlw 0x00 + retlw 0x30 + retlw 0x20 + retlw 0x50 + retlw 0x40 + retlw 0x70 + retlw 0x60 + retlw 0x83 + retlw 0x93 + retlw 0xA3 + retlw 0xB3 + retlw 0xC3 + retlw 0xD3 + retlw 0xE3 + retlw 0xF3 + retlw 0x02 + retlw 0x12 + retlw 0x22 + retlw 0x32 + retlw 0x42 + retlw 0x52 + retlw 0x62 + retlw 0x72 + retlw 0xB5 + retlw 0xA5 + retlw 0x95 + retlw 0x85 + retlw 0xF5 + retlw 0xE5 + retlw 0xD5 + retlw 0xC5 + retlw 0x34 + retlw 0x24 + retlw 0x14 + retlw 0x04 + retlw 0x74 + retlw 0x64 + retlw 0x54 + retlw 0x44 + retlw 0xA7 + retlw 0xB7 + retlw 0x87 + retlw 0x97 + retlw 0xE7 + retlw 0xF7 + retlw 0xC7 + retlw 0xD7 + retlw 0x26 + retlw 0x36 + retlw 0x06 + retlw 0x16 + retlw 0x66 + retlw 0x76 + retlw 0x46 + retlw 0x56 + retlw 0xD9 + retlw 0xC9 + retlw 0xF9 + retlw 0xE9 + retlw 0x99 + retlw 0x89 + retlw 0xB9 + retlw 0xA9 + retlw 0x58 + retlw 0x48 + retlw 0x78 + retlw 0x68 + retlw 0x18 + retlw 0x08 + retlw 0x38 + retlw 0x28 + retlw 0xCB + retlw 0xDB + retlw 0xEB + retlw 0xFB + retlw 0x8B + retlw 0x9B + retlw 0xAB + retlw 0xBB + retlw 0x4A + retlw 0x5A + retlw 0x6A + retlw 0x7A + retlw 0x0A + retlw 0x1A + retlw 0x2A + retlw 0x3A + retlw 0xFD + retlw 0xED + retlw 0xDD + retlw 0xCD + retlw 0xBD + retlw 0xAD + retlw 0x9D + retlw 0x8D + retlw 0x7C + retlw 0x6C + retlw 0x5C + retlw 0x4C + retlw 0x3C + retlw 0x2C + retlw 0x1C + retlw 0x0C + retlw 0xEF + retlw 0xFF + retlw 0xCF + retlw 0xDF + retlw 0xAF + retlw 0xBF + retlw 0x8F + retlw 0x9F + retlw 0x6E + retlw 0x7E + retlw 0x4E + retlw 0x5E + retlw 0x2E + retlw 0x3E + retlw 0x0E + retlw 0x1E diff --git a/firmware1704/crc16lo.inc b/firmware1704/crc16lo.inc new file mode 100644 index 0000000..f06bc81 --- /dev/null +++ b/firmware1704/crc16lo.inc @@ -0,0 +1,257 @@ +;LUT for low byte of CCITT CRC16 + retlw 0x00 + retlw 0x21 + retlw 0x42 + retlw 0x63 + retlw 0x84 + retlw 0xA5 + retlw 0xC6 + retlw 0xE7 + retlw 0x08 + retlw 0x29 + retlw 0x4A + retlw 0x6B + retlw 0x8C + retlw 0xAD + retlw 0xCE + retlw 0xEF + retlw 0x31 + retlw 0x10 + retlw 0x73 + retlw 0x52 + retlw 0xB5 + retlw 0x94 + retlw 0xF7 + retlw 0xD6 + retlw 0x39 + retlw 0x18 + retlw 0x7B + retlw 0x5A + retlw 0xBD + retlw 0x9C + retlw 0xFF + retlw 0xDE + retlw 0x62 + retlw 0x43 + retlw 0x20 + retlw 0x01 + retlw 0xE6 + retlw 0xC7 + retlw 0xA4 + retlw 0x85 + retlw 0x6A + retlw 0x4B + retlw 0x28 + retlw 0x09 + retlw 0xEE + retlw 0xCF + retlw 0xAC + retlw 0x8D + retlw 0x53 + retlw 0x72 + retlw 0x11 + retlw 0x30 + retlw 0xD7 + retlw 0xF6 + retlw 0x95 + retlw 0xB4 + retlw 0x5B + retlw 0x7A + retlw 0x19 + retlw 0x38 + retlw 0xDF + retlw 0xFE + retlw 0x9D + retlw 0xBC + retlw 0xC4 + retlw 0xE5 + retlw 0x86 + retlw 0xA7 + retlw 0x40 + retlw 0x61 + retlw 0x02 + retlw 0x23 + retlw 0xCC + retlw 0xED + retlw 0x8E + retlw 0xAF + retlw 0x48 + retlw 0x69 + retlw 0x0A + retlw 0x2B + retlw 0xF5 + retlw 0xD4 + retlw 0xB7 + retlw 0x96 + retlw 0x71 + retlw 0x50 + retlw 0x33 + retlw 0x12 + retlw 0xFD + retlw 0xDC + retlw 0xBF + retlw 0x9E + retlw 0x79 + retlw 0x58 + retlw 0x3B + retlw 0x1A + retlw 0xA6 + retlw 0x87 + retlw 0xE4 + retlw 0xC5 + retlw 0x22 + retlw 0x03 + retlw 0x60 + retlw 0x41 + retlw 0xAE + retlw 0x8F + retlw 0xEC + retlw 0xCD + retlw 0x2A + retlw 0x0B + retlw 0x68 + retlw 0x49 + retlw 0x97 + retlw 0xB6 + retlw 0xD5 + retlw 0xF4 + retlw 0x13 + retlw 0x32 + retlw 0x51 + retlw 0x70 + retlw 0x9F + retlw 0xBE + retlw 0xDD + retlw 0xFC + retlw 0x1B + retlw 0x3A + retlw 0x59 + retlw 0x78 + retlw 0x88 + retlw 0xA9 + retlw 0xCA + retlw 0xEB + retlw 0x0C + retlw 0x2D + retlw 0x4E + retlw 0x6F + retlw 0x80 + retlw 0xA1 + retlw 0xC2 + retlw 0xE3 + retlw 0x04 + retlw 0x25 + retlw 0x46 + retlw 0x67 + retlw 0xB9 + retlw 0x98 + retlw 0xFB + retlw 0xDA + retlw 0x3D + retlw 0x1C + retlw 0x7F + retlw 0x5E + retlw 0xB1 + retlw 0x90 + retlw 0xF3 + retlw 0xD2 + retlw 0x35 + retlw 0x14 + retlw 0x77 + retlw 0x56 + retlw 0xEA + retlw 0xCB + retlw 0xA8 + retlw 0x89 + retlw 0x6E + retlw 0x4F + retlw 0x2C + retlw 0x0D + retlw 0xE2 + retlw 0xC3 + retlw 0xA0 + retlw 0x81 + retlw 0x66 + retlw 0x47 + retlw 0x24 + retlw 0x05 + retlw 0xDB + retlw 0xFA + retlw 0x99 + retlw 0xB8 + retlw 0x5F + retlw 0x7E + retlw 0x1D + retlw 0x3C + retlw 0xD3 + retlw 0xF2 + retlw 0x91 + retlw 0xB0 + retlw 0x57 + retlw 0x76 + retlw 0x15 + retlw 0x34 + retlw 0x4C + retlw 0x6D + retlw 0x0E + retlw 0x2F + retlw 0xC8 + retlw 0xE9 + retlw 0x8A + retlw 0xAB + retlw 0x44 + retlw 0x65 + retlw 0x06 + retlw 0x27 + retlw 0xC0 + retlw 0xE1 + retlw 0x82 + retlw 0xA3 + retlw 0x7D + retlw 0x5C + retlw 0x3F + retlw 0x1E + retlw 0xF9 + retlw 0xD8 + retlw 0xBB + retlw 0x9A + retlw 0x75 + retlw 0x54 + retlw 0x37 + retlw 0x16 + retlw 0xF1 + retlw 0xD0 + retlw 0xB3 + retlw 0x92 + retlw 0x2E + retlw 0x0F + retlw 0x6C + retlw 0x4D + retlw 0xAA + retlw 0x8B + retlw 0xE8 + retlw 0xC9 + retlw 0x26 + retlw 0x07 + retlw 0x64 + retlw 0x45 + retlw 0xA2 + retlw 0x83 + retlw 0xE0 + retlw 0xC1 + retlw 0x1F + retlw 0x3E + retlw 0x5D + retlw 0x7C + retlw 0x9B + retlw 0xBA + retlw 0xD9 + retlw 0xF8 + retlw 0x17 + retlw 0x36 + retlw 0x55 + retlw 0x74 + retlw 0x93 + retlw 0xB2 + retlw 0xD1 + retlw 0xF0 diff --git a/firmware1704/crc7.inc b/firmware1704/crc7.inc new file mode 100644 index 0000000..73477a3 --- /dev/null +++ b/firmware1704/crc7.inc @@ -0,0 +1,257 @@ +;LUT for CRC7 for card commands + retlw 0x00 + retlw 0x12 + retlw 0x24 + retlw 0x36 + retlw 0x48 + retlw 0x5A + retlw 0x6C + retlw 0x7E + retlw 0x90 + retlw 0x82 + retlw 0xB4 + retlw 0xA6 + retlw 0xD8 + retlw 0xCA + retlw 0xFC + retlw 0xEE + retlw 0x32 + retlw 0x20 + retlw 0x16 + retlw 0x04 + retlw 0x7A + retlw 0x68 + retlw 0x5E + retlw 0x4C + retlw 0xA2 + retlw 0xB0 + retlw 0x86 + retlw 0x94 + retlw 0xEA + retlw 0xF8 + retlw 0xCE + retlw 0xDC + retlw 0x64 + retlw 0x76 + retlw 0x40 + retlw 0x52 + retlw 0x2C + retlw 0x3E + retlw 0x08 + retlw 0x1A + retlw 0xF4 + retlw 0xE6 + retlw 0xD0 + retlw 0xC2 + retlw 0xBC + retlw 0xAE + retlw 0x98 + retlw 0x8A + retlw 0x56 + retlw 0x44 + retlw 0x72 + retlw 0x60 + retlw 0x1E + retlw 0x0C + retlw 0x3A + retlw 0x28 + retlw 0xC6 + retlw 0xD4 + retlw 0xE2 + retlw 0xF0 + retlw 0x8E + retlw 0x9C + retlw 0xAA + retlw 0xB8 + retlw 0xC8 + retlw 0xDA + retlw 0xEC + retlw 0xFE + retlw 0x80 + retlw 0x92 + retlw 0xA4 + retlw 0xB6 + retlw 0x58 + retlw 0x4A + retlw 0x7C + retlw 0x6E + retlw 0x10 + retlw 0x02 + retlw 0x34 + retlw 0x26 + retlw 0xFA + retlw 0xE8 + retlw 0xDE + retlw 0xCC + retlw 0xB2 + retlw 0xA0 + retlw 0x96 + retlw 0x84 + retlw 0x6A + retlw 0x78 + retlw 0x4E + retlw 0x5C + retlw 0x22 + retlw 0x30 + retlw 0x06 + retlw 0x14 + retlw 0xAC + retlw 0xBE + retlw 0x88 + retlw 0x9A + retlw 0xE4 + retlw 0xF6 + retlw 0xC0 + retlw 0xD2 + retlw 0x3C + retlw 0x2E + retlw 0x18 + retlw 0x0A + retlw 0x74 + retlw 0x66 + retlw 0x50 + retlw 0x42 + retlw 0x9E + retlw 0x8C + retlw 0xBA + retlw 0xA8 + retlw 0xD6 + retlw 0xC4 + retlw 0xF2 + retlw 0xE0 + retlw 0x0E + retlw 0x1C + retlw 0x2A + retlw 0x38 + retlw 0x46 + retlw 0x54 + retlw 0x62 + retlw 0x70 + retlw 0x82 + retlw 0x90 + retlw 0xA6 + retlw 0xB4 + retlw 0xCA + retlw 0xD8 + retlw 0xEE + retlw 0xFC + retlw 0x12 + retlw 0x00 + retlw 0x36 + retlw 0x24 + retlw 0x5A + retlw 0x48 + retlw 0x7E + retlw 0x6C + retlw 0xB0 + retlw 0xA2 + retlw 0x94 + retlw 0x86 + retlw 0xF8 + retlw 0xEA + retlw 0xDC + retlw 0xCE + retlw 0x20 + retlw 0x32 + retlw 0x04 + retlw 0x16 + retlw 0x68 + retlw 0x7A + retlw 0x4C + retlw 0x5E + retlw 0xE6 + retlw 0xF4 + retlw 0xC2 + retlw 0xD0 + retlw 0xAE + retlw 0xBC + retlw 0x8A + retlw 0x98 + retlw 0x76 + retlw 0x64 + retlw 0x52 + retlw 0x40 + retlw 0x3E + retlw 0x2C + retlw 0x1A + retlw 0x08 + retlw 0xD4 + retlw 0xC6 + retlw 0xF0 + retlw 0xE2 + retlw 0x9C + retlw 0x8E + retlw 0xB8 + retlw 0xAA + retlw 0x44 + retlw 0x56 + retlw 0x60 + retlw 0x72 + retlw 0x0C + retlw 0x1E + retlw 0x28 + retlw 0x3A + retlw 0x4A + retlw 0x58 + retlw 0x6E + retlw 0x7C + retlw 0x02 + retlw 0x10 + retlw 0x26 + retlw 0x34 + retlw 0xDA + retlw 0xC8 + retlw 0xFE + retlw 0xEC + retlw 0x92 + retlw 0x80 + retlw 0xB6 + retlw 0xA4 + retlw 0x78 + retlw 0x6A + retlw 0x5C + retlw 0x4E + retlw 0x30 + retlw 0x22 + retlw 0x14 + retlw 0x06 + retlw 0xE8 + retlw 0xFA + retlw 0xCC + retlw 0xDE + retlw 0xA0 + retlw 0xB2 + retlw 0x84 + retlw 0x96 + retlw 0x2E + retlw 0x3C + retlw 0x0A + retlw 0x18 + retlw 0x66 + retlw 0x74 + retlw 0x42 + retlw 0x50 + retlw 0xBE + retlw 0xAC + retlw 0x9A + retlw 0x88 + retlw 0xF6 + retlw 0xE4 + retlw 0xD2 + retlw 0xC0 + retlw 0x1C + retlw 0x0E + retlw 0x38 + retlw 0x2A + retlw 0x54 + retlw 0x46 + retlw 0x70 + retlw 0x62 + retlw 0x8C + retlw 0x9E + retlw 0xA8 + retlw 0xBA + retlw 0xC4 + retlw 0xD6 + retlw 0xE0 + retlw 0xF2 diff --git a/firmware1704/firmware1704.asm b/firmware1704/firmware1704.asm new file mode 100644 index 0000000..71bdceb --- /dev/null +++ b/firmware1704/firmware1704.asm @@ -0,0 +1,2096 @@ +;;; 80 characters wide please ;;;;;;;;;;;;;;;;;;;;;;;;;; 8-space tabs please ;;; + + +; +;;; +;;;;; TashTwenty: Single-Chip DCD Interface +;;; +; + + +;;; Connections ;;; + +;;; ;;; +; .--------. ; +; Supply -|01 \/ 14|- Ground ; +; !ENBL ---> RA5 -|02 13|- RA0 ---> Next !ENBL ; +; PH3 ---> RA4 -|03 12|- RA1 <--- PH0 ; +; PH2 ---> RA3 -|04 11|- RA2 <--- PH1 ; +; WR ---> RC5 -|05 10|- RC0 ---> MMC SCK ; +; RD <--- RC4 -|06 09|- RC1 <--- MMC MISO ; +; MMC !CS <--- RC3 -|07 08|- RC2 ---> MMC MOSI ; +; '--------' ; +;;; ;;; + + +;;; Assembler Directives ;;; + + list P=PIC16F1704, F=INHX32, ST=OFF, MM=OFF, R=DEC, X=ON + #include P16F1704.inc + __config _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF + ;_FOSC_INTOSC Internal oscillator, I/O on RA5 + ;_WDTE_OFF Watchdog timer disabled + ;_PWRTE_ON Keep in reset for 64 ms on start + ;_MCLRE_OFF RA3/!MCLR is RA3 + ;_CP_OFF Code protection off + ;_BOREN_OFF Brownout reset off + ;_CLKOUTEN_OFF CLKOUT disabled, I/O on RA4 + ;_IESO_OFF Internal/External switch not needed + ;_FCMEN_OFF Fail-safe clock monitor not needed + __config _CONFIG2, _WRT_OFF & _PPS1WAY_OFF & _ZCDDIS_ON & _PLLEN_ON & _STVREN_ON & _LVP_OFF + ;_WRT_OFF Write protection off + ;_PPS1WAY_OFF PPS can change more than once + ;_ZCDDIS_ON Zero crossing detector disabled + ;_PLLEN_ON 4x PLL on + ;_STVREN_ON Stack over/underflow causes reset + ;_LVP_OFF High-voltage on Vpp to program + + +;;; Macros ;;; + +DELAY macro value ;Delay 3*W cycles, set W to 0 + movlw value + decfsz WREG,F + bra $-1 + endm + +DNOP macro ;Double-NOP + bra $+1 + endm + + +;;; Constants ;;; + +EN_PORT equ PORTA +EN_PIN equ RA5 +NE_PORT equ PORTA +NE_PIN equ RA0 +PH_PORT equ PORTA +PH_IOC equ IOCAF +PH0_PIN equ RA1 +PH1_PIN equ RA2 +PH2_PIN equ RA3 +PH3_PIN equ RA4 +WR_PORT equ PORTC +WR_IOC equ IOCCF +WR_PIN equ RC5 +CS_PORT equ PORTC +CS_PIN equ RC3 + +ERRCODE equ 0x80 ;Code returned in "stat" field when there's an error + +;IWMFLAG: +IWMBYTE equ 7 ;Cleared when a byte has been received from IWM +IWMOVER equ 6 ;Set on transition to state 2 or 3 while IWM receiving +IWMTEMP equ 5 ;Used as temporary storage by ISR +IWMCNT1 equ 1 ;Counter for how many times the ISR was called +IWMCNT0 equ 0 ; " + +;M_FLAGS: +M_FAIL equ 7 ;Set when there's been a failure on the MMC interface +M_CMDLR equ 6 ;Set when R3 or R7 is expected (5 bytes), not R1 +M_CMDRB equ 5 ;Set when an R1b is expected (busy signal after reply) +M_MBRD equ 4 ;Set when a multiblock read is in progress +M_MBWR equ 3 ;Set when a multiblock write is in progress +M_BKADR equ 2 ;Set when block (rather than byte) addressing is in use +M_CDVER equ 1 ;Set when dealing with a V2.0+ card, clear for 1.0 + + +;;; Variable Storage ;;; + + cblock 0x20 ;Bank 0 registers + + DEVNUM ;Selected device number + DEVMAX ;Maximum device number + IWMFLAG ;IWM receiver flags + IWMFSAP ;IWM post-processing FSA pointer + IWMLSBS ;LSB shift register for received or transmitted IWM bytes + IWMREXP ;Size in groups of expected response to command + CHKSUM ;Checksum + CMDNUM ;Command number + CMDCNT ;Command block count + CMDHIGH ;Command high address byte + CMDMED ;Command middle address byte + CMDLOW ;Command low address byte + RESPERR ;Command response status byte + + endc + + cblock 0x70 ;Bank-common registers + + IWMSR ;Shift register for incoming data from IWM + M_FLAGS ;MMC flags + M_CMDN ;The MMC command to be sent, later the R1 response byte + M_ADR3 ;First (high) byte of the address, first byte of R3/R7 response + M_ADR2 ;Second byte of the address, second byte of R3/R7 response + M_ADR1 ;Third byte of the address, third byte of R3/R7 response + M_ADR0 ;Fourth (low) byte of the address, last byte of R3/R7 response + M_CRCH ;CRC16 high byte + M_CRCL ;CRC16 low byte + X6 ;Various purposes + X5 ;Various purposes + X4 ;Various purposes + X3 ;Various purposes + X2 ;Various purposes + X1 ;Various purposes + X0 ;Various purposes + + endc + + +;;; Vectors ;;; + + org 0x0 ;Reset vector + movlp high Init + goto Init + + org 0x4 ;Interrupt vector + ;fall through + + +;;; Interrupt Handler ;;; + +Interrupt + btfsc INTCON,INTF ;If we're here because of a rising edge on + bra IntDisable ; !ENBL, go take care of that + lslf IWMSR,W ;Make space for three new bits in the IWM shift + lslf WREG,W ; register + lslf WREG,W ; " + movlb 30 ;Grab the bits off the two-bit shift register + iorwf CLCDATA,W ; and put them into the IWM shift register (we + movwf IWMSR ; know the LSB of CLCDATA will be low because + movlb 0 ; !HSHK is low whenever we're receiving) + incf IWMFLAG,F ;Increment the counter of times the interrupt + btfss IWMFLAG,1 ; handler was called; if this is the first of + bra IntFirst ; two, skip ahead + movlb 7 ;Clear any triggered edge on WR so we can spin + bcf WR_IOC,WR_PIN ; and wait for one later + movlb 0 ; " + btfsc WR_PORT,WR_PIN ;Grab the current state of WR and put that into + bsf IWMSR,0 ; bit 0 of the IWM shift register + btfss INTCON,IOCIF ;Spin until we get an edge on WR or a rising + bra $-1 ; edge on PH1 (a transition to state 2 or 3) + nop ;Delay + clrf IWMFLAG ;Signal we have a byte and reset call counter + movlw high LutInv|0x80;Point to inversion LUT for translation ahead + movwf FSR0H ; " + btfsc WR_PORT,WR_PIN ;Grab the current state of WR (first bit of a + bsf IWMFLAG,IWMTEMP ; new cell) and put that into our temp bit + clrf TMR2 ;Re-calibrate Timer2 + movf IWMSR,W ;Use the inversion LUT to change the high/low + movwf FSR0L ; levels into transitions or non-transitions + movf INDF0,W ; and turn it into an IWM byte and write that + movwf INDF1 ; to the IWM queue + btfss PH_PORT,PH0_PIN ;If PH0 is clear, clear the MSB of the byte we + bcf INDF1,7 ; just write to indicate a suspend is on + clrf IWMSR ;Clear the IWM shift register and put the first + btfsc IWMFLAG,IWMTEMP ; bit of the new cell that we saved in the temp + bsf IWMSR,1 ; bit into bit 1 of it + movlw -33 ;Set Timer0 to interrupt again after 33 cycles + movwf TMR0 ; and clear its interrupt flag + bcf INTCON,TMR0IF ; " + DNOP ;Delay to get the read at a better position + btfsc WR_PORT,WR_PIN ;Grab the current state of WR and put that into + bsf IWMSR,0 ; bit 0 of the IWM shift register + movlb 7 ;If we were released from the loop by a + btfsc PH_IOC,PH1_PIN ; transition to state 2 or 3, we're done + bra IntDataOver ; receiving data and must set flags accordingly + movlb 0 ;Otherwise, post process this byte + bra IntPostProc ; " + +IntFirst + btfsc WR_PORT,WR_PIN ;Grab the current state of WR and put that into + bsf IWMSR,0 ; bit 0 of the IWM shift register + movlw -27 ;Set Timer0 to interrupt again after 27 cycles + movwf TMR0 ; " + bcf INTCON,TMR0IF ;Clear Timer0 interrupt flag + retfie + +IntDataOver + movlb 0 ;Clear the IWM byte-received bit and set the + clrf IWMFLAG ; IWM data over flag to communicate the + bsf IWMFLAG,IWMOVER ; transition to state 2 or 3 + bcf INTCON,TMR0IE ;Disable the Timer0 interrupt + ;fall through + +IntPostProc + movf IWMFSAP,W + brw +IPP +IPPJunk movlw IPPSync - IPP ;The first byte 'received' is junk, proceed to + movwf IWMFSAP ; expect a sync byte + retfie +IPPSync movlw 0xAA ;If the byte received was a sync byte (0xAA), + xorwf INDF1,W ; advance to expect the group count, otherwise + movlw IPPTCnt - IPP ; remain in this state + btfsc STATUS,Z ; " + movwf IWMFSAP ; " + retfie +IPPTCnt movlw IPPRCnt - IPP ;The byte received is the transmit group count, + movwf IWMFSAP ; throw it away as we don't use it + retfie +IPPRCnt movf INDF1,W ;The byte received is the expected response + andlw B'01111111' ; size count, save it in case it's needed + movwf IWMREXP ; " + movlw IPPLsbs - IPP ;Next byte will be the LSBs of the following + movwf IWMFSAP ; seven bytes + retfie +IPPLsbs movf INDF1,W ;The byte received is the LSBs of the following + movwf IWMLSBS ; seven bytes, keep it to shift out later + movlw IPPByt1 - IPP ;Next byte will be first data byte in group + movwf IWMFSAP ; " + retfie +IPPByt1 lsrf IWMLSBS,F ;Shift out the first LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPByt2 - IPP ;Next byte will be second data byte in group + movwf IWMFSAP ; " + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPByt2 lsrf IWMLSBS,F ;Shift out the next LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPByt3 - IPP ;Next byte will be third data byte in group + movwf IWMFSAP ; " + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPByt3 lsrf IWMLSBS,F ;Shift out the next LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPByt4 - IPP ;Next byte will be fourth data byte in group + movwf IWMFSAP ; " + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPByt4 lsrf IWMLSBS,F ;Shift out the next LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPByt5 - IPP ;Next byte will be fifth data byte in group + movwf IWMFSAP ; " + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPByt5 lsrf IWMLSBS,F ;Shift out the next LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPByt6 - IPP ;Next byte will be sixth data byte in group + movwf IWMFSAP ; " + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPByt6 lsrf IWMLSBS,F ;Shift out the next LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPByt7 - IPP ;Next byte will be seventh data byte in group + btfss STATUS,C ; but if we're in a suspend at this point, make + movlw IPPSus7 - IPP ; sure the byte after the seventh is treated + movwf IWMFSAP ; as a restart sync byte + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPByt7 lsrf IWMLSBS,F ;Shift out the last LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPLsbs - IPP ;Next byte will be LSBs of the next group + btfss STATUS,C ; unless we're in a suspend, in which case it + movlw IPPRSyn - IPP ; will be a restart sync byte + movwf IWMFSAP ; " + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPSus7 lsrf IWMLSBS,F ;Shift out the last LSB and rotate it into the + rlf INDF1,F ; received byte + movlw IPPRSyn - IPP ;A suspend was detected at the end of the sixth + movwf IWMFSAP ; byte, so next byte is a restart sync byte + movlb 31 ;Increment and wrap pointer + incf FSR1L_SHAD,F ; " + bcf FSR1L_SHAD,7 ; " + retfie +IPPRSyn movlw 0xAA ;If the byte received was a sync byte (0xAA), + xorwf INDF1,W ; advance to expect the LSBs of the next group, + movlw IPPLsbs - IPP ; otherwise remain in this state + btfsc STATUS,Z ; " + movwf IWMFSAP ; " + retfie + +IntDisable + movlw B'00110010' ;Tristate RD + tris 7 ; " + movlb 0 ;Deassert !ENBL for the next external device + bsf NE_PORT,NE_PIN ; " + bcf INTCON,INTF ;Clear the rising !ENBL interrupt + retfie + + +;;; Peripheral Initialization ;;; + +Init + banksel OSCCON ;32 MHz (w/PLL) high-freq internal oscillator + movlw B'11110000' + movwf OSCCON + + banksel SSPCON1 ;SSP SPI master mode, clock set by baud rate + movlw B'00101010' ; generator to 400 kHz, clock idles low, data + movwf SSPCON1 ; lines change on falling edge, data sampled on + movlw B'01000000' ; rising edge (CKP=0, CKE=1, SMP=0) + movwf SSP1STAT + movlw 19 + movwf SSP1ADD + + banksel BAUDCON ;USART in synchronous mode, data clocked on + movlw B'01011000' ; rising edge, baud rate 500 kHz for IWM, and + movwf BAUDCON ; transmit all zeroes so RD line starts high + clrf SPBRGH + movlw 15 + movwf SPBRGL + movlw B'10110110' + movwf TXSTA + movlw B'10000000' + movwf RCSTA + clrf TXREG + + banksel CLC1CON + movlw 0x15 ;DT ; ____ + movwf CLC1SEL0 ; DT --| \ ____ + movlw 0x14 ;CK ; | O---------| \__ + movwf CLC1SEL1 ; CK --|____/ !PH2 --|____/ | ____ + movlw 0x01 ;CLCIN1/PH1; `--\ \___ RD + movwf CLC1SEL2 ; ____ ,--/___/ + movlw 0x02 ;CLCIN2/PH2; PH1 --| \__| + movwf CLC1SEL3 ; PH2 --|____/ + movlw B'00000101' + movwf CLC1GLS0 + movlw B'01000000' + movwf CLC1GLS1 + movlw B'00100000' + movwf CLC1GLS2 + movlw B'10000000' + movwf CLC1GLS3 + clrf CLC1POL + movlw B'10000000' + movwf CLC1CON + movlw 0x1A ;T2_match ; 0__ 0__ + movwf CLC2SEL0 ; __|__ __|__ + movlw 0x03 ;CLCIN3/WR ; | S | | S | + movwf CLC2SEL1 ; WR --|D Q|-------------|D Q| + movlw B'00000010' ; | | | | + movwf CLC2GLS0 ; T2_match --|> | T2_match --|> | + movlw B'00001000' ; |__R__| |__R__| + movwf CLC2GLS1 ; __| __| + clrf CLC2GLS2 ; 0 0 + clrf CLC2GLS3 ; CLC2 CLC3 + clrf CLC2POL + movlw B'10000100' + movwf CLC2CON + movlw 0x1A ;T2_match + movwf CLC3SEL0 + movlw 0x05 ;LC2_out + movwf CLC3SEL1 + movlw B'00000010' + movwf CLC3GLS0 + movlw B'00001000' + movwf CLC3GLS1 + clrf CLC3GLS2 + clrf CLC3GLS3 + clrf CLC3POL + movlw B'10000100' + movwf CLC3CON + + banksel T2CON ;Timer2 1:1 with instruction clock and matches + movlw 15 ; every 16 cycles + movwf PR2 + movlw B'00000100' + movwf T2CON + + banksel OPTION_REG ;Timer0 1:1 with instruction clock, INT reacts + movlw B'11011111' ; to rising edges + movwf OPTION_REG + + banksel CLCIN0PPS ;CLCIN1=RA2=PH1, CLCIN2=RA3=PH2, CLCIN3=RC5=WR, + movlw B'00000010' ;MISO=RC1, INT=RA5=!ENBL + movwf CLCIN1PPS + movlw B'00000011' + movwf CLCIN2PPS + movlw B'00010101' + movwf CLCIN3PPS + movlw B'00010001' + movwf SSPDATPPS + movlw B'00000101' + movwf INTPPS + + banksel RC4PPS ;RC4=LC1OUT, RC2=MOSI, RC0=SCK + movlw B'00000100' + movwf RC4PPS + movlw B'00010010' + movwf RC2PPS + movlw B'00010000' + movwf RC0PPS + + banksel ANSELA ;All pins digital, not analog + clrf ANSELA + clrf ANSELC + + banksel LATA ;Next !ENBL and !CS high + movlw B'00111111' + movwf LATA + movwf LATC + + banksel TRISA ;Next !ENBL, !CS, SCK, MOSI outputs, RD output + movlw B'00111110' ; but tristated for now, MISO, WR, PH3-0, !ENBL + movwf TRISA ; inputs + movlw B'00110010' + movwf TRISC + + banksel INLVLA ;TTL instead of Schmitt trigger for input ports + clrf INLVLA + clrf INLVLC + + movlw 12 ;Delay approximately 2 ms at an instruction + movwf X0 ; clock of 2 MHz until the PLL kicks in and the +PllWait DELAY 110 ; instruction clock gears up to 8 MHz + decfsz X0,F + bra PllWait + + movlw B'00010000' ;INT interrupts on + movwf INTCON + + movlp 8 + goto SysInit + + +;;; Error Handler ;;; + +ErrorHandler + movwf X0 ;Save the error code that brought us here + bcf INTCON,GIE ;Make sure interrupts are off + movlw B'00110010' ;Tristate RD (this is probably not necessary, + tris 7 ; but.. belt and suspenders) + banksel RC4PPS ;Return RD to be driven by its latch so it's + clrf RC4PPS ; always high (doesn't matter really) + banksel CLCIN0PPS ;Reconfigure CLC1 to pass through !ENBL to the + movlw B'00000101' ; next device in the chain so we don't have + movwf CLCIN0PPS ; to do this in software + banksel CLC1CON ; " + clrf CLC1SEL0 ; " + movlw B'00000010' ; " + movwf CLC1GLS0 ; " + clrf CLC1GLS1 ; " + clrf CLC1GLS2 ; " + clrf CLC1GLS3 ; " + movlw B'10000001' ; " + movwf CLC1CON ; " + banksel RA0PPS ; " + movlw B'00000100' ; " + movwf RA0PPS ; " + movlb 0 ; " +ErrHan0 movlw 10 ;Delay five seconds between bursts of blinking + movwf X1 ; " +ErrHan1 call OneHalfSecond ; " + decfsz X1,F ; " + bra ErrHan1 ; " + movf X0,W ;Move the error code into the shift register + movwf X1 ; and shift the MSB into carry while setting + lslf X1,F ; the LSB as an end sentinel + bsf X1,0 ; " +ErrHan2 bcf CS_PORT,CS_PIN ;Lower !CS for 0.5s for a 1 or 1.5s for a 0, + call OneHalfSecond ; in either case making the bit cell 2s + btfsc STATUS,C ; " + bsf CS_PORT,CS_PIN ; " + call OneHalfSecond ; " + call OneHalfSecond ; " + bsf CS_PORT,CS_PIN ; " + call OneHalfSecond ; " + lslf X1,F ;Shift the next bit into carry; if the bit we + btfsc STATUS,Z ; just blinked out was the last, go back to + bra ErrHan0 ; delay and start over, else blink out the next + bra ErrHan2 ; bit + +OneHalfSecond + movlw 20 ;Delay 3947587 cycles (including call), which + movwf X3 ; is a little less than a half second + clrf X2 ; " +OneHal0 DELAY 0 ; " + decfsz X2,F ; " + bra OneHal0 ; " + decfsz X3,F ; " + bra OneHal0 ; " + return ; " + + +;;; Mainline ;;; + +Unselected + movlw B'00110010' ;Tristate RD (the interrupt handler will have + tris 7 ; done this already, but.. belt and suspenders) + movlb 3 ;Deassert !HSHK + clrf TXREG ; " + movlb 0 ;Deassert !CS and next !ENBL (the interrupt + movlw B'00111111' ; handler will have deasserted next !ENBL + movwf PORTC ; already, but.. belt and suspenders) + movlw B'00111111' ; " + movwf PORTA ; " + clrf DEVNUM ;Reset selected device number to zero + btfsc EN_PORT,EN_PIN ;Wait for Mac to drive !ENBL low + bra $-1 ; " + movlw B'00100010' ;Drive RD + tris 7 ; " + ;fall through + +IdleLoop + movf PORTA,W + brw + nop + bra IdleLoop ;HOFF asserted, but there's nothing to holdoff + nop ; from, so wait for a state change + bra IdleLoop ;Data mode, but we haven't done a handshake, so + nop ; wait for a state change + bra IdleLoop ;Idle state, we can do that + nop ; " + movlp 8 ;HOST asserted, Mac wants to talk, so let's go + goto Receiver ; make that happen + bra IdleLoop ;RESET asserted, we're already idling, so how + nop ; much more reset can we get + bra IdleLoop ;Mac wants us to drive RD low, the CLC will + nop ; take care of that, so wait for state change + bra IdleLoop ;Mac wants us to drive RD high, the CLC will + nop ; take care of that, so wait for state change + bra IdleLoop ;Mac wants us to drive RD high, the CLC will + nop ; take care of that, so wait for state change + bra NextDevice ;Mac wants to select the next device + nop ; " + bra NextDevice ;Mac wants to select the next device + nop ; " + bra NextDevice ;Mac wants to select the next device + nop ; " + bra NextDevice ;Mac wants to select the next device + nop ; " + bra NextDevice ;Mac wants to select the next device + nop ; " + bra NextDevice ;Mac wants to select the next device + nop ; " + bra NextDevice ;Mac wants to select the next device + nop ; " + bra NextDevice ;Mac wants to select the next device + nop ; " + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + bra Unselected ;Mac has unselected us, do necessary things + nop ; and wait to be selected again + +NextDevice + movf DEVNUM,W ;If we've already selected the maximum device + xorwf DEVMAX,W ; number, select the daisy chained device + btfsc STATUS,Z ; downstream from us + bra NoMoreDevices ; " + incf DEVNUM,F ;Select the next emulated device number + btfsc PH_PORT,PH3_PIN ;Wait until the PH3 pin pulse has ended + bra $-1 ; " + bra IdleLoop ;Return to idle loop + +NoMoreDevices + movlw B'00110010' ;Tristate RD + tris 7 ; " + bcf NE_PORT,NE_PIN ;Drive the next !ENBL low + btfss EN_PORT,EN_PIN ;Wait for !ENBL to go high + bra $-1 ; " + bra Unselected ;We've been unselected, take care of that + + +;;; Lookup Tables ;;; + + org 0x200 + +;The TashTwenty logo in the form of a 32x32 bitmap icon with mask +LutIcon + #include icon.inc + + + org 0x300 + +;LUT for translating IWM inversions into IWM bytes +LutInv + #include inv.inc + + + org 0x400 + +;LUT for flipping bytes left to right for use with USART as IWM transmitter +LutFlip + #include flip.inc + + + org 0x500 + +;LUT for high byte of CCITT CRC16 +LutCrc7 + #include crc7.inc + + + org 0x600 + +;LUT for high byte of CCITT CRC16 +LutCrc16H + #include crc16hi.inc + + + org 0x700 + +;LUT for low byte of CCITT CRC16 +LutCrc16L + #include crc16lo.inc + + + org 0x800 + +;;; System Initialization ;;; + +GoError + movlp 0 ;Something went wrong, so pass !ENBL through + goto ErrorHandler ; and try to let the user know what happened + +SysInit + call MmcInit ;Try to initialize the card + btfsc M_FLAGS,M_FAIL ;If card initialization failed, pass the error + bra GoError ; code to the error handler + movlb 4 ;Since initialization succeeded, crank the + movlw B'00100001' ; speed of the SPI interface up to 2 MHz + movwf SSPCON1 ; " + movlb 0 ; " + movlw 4 ;Try to read the MBR up to four times which is, + movwf X2 ; frankly, excessive +SysIni0 bsf CS_PORT,CS_PIN ;Deassert !CS + movlw 0x51 ;Set up a read command for the card (R1-type + movwf M_CMDN ; response) + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + clrf M_ADR3 ;Point the card address to the master boot + clrf M_ADR2 ; record + clrf M_ADR1 ; " + clrf M_ADR0 ; " + bcf CS_PORT,CS_PIN ;Assert !CS + call MmcCmd ;Send the read command + movf M_CMDN,W ;Treat any error flag as a failed command + andlw B'11111110' ; " + btfss STATUS,Z ; " + bsf M_FLAGS,M_FAIL ; " + movlw 10 ;If the operation failed, give up and pass a + btfsc M_FLAGS,M_FAIL ; code to the error handler + bra GoError ; " + call MmcGetData ;Get the card ready to read out data + movlw 11 ;If the operation failed, give up and pass a + btfsc M_FLAGS,M_FAIL ; code to the error handler + bra GoError ; " + clrf M_CRCH ;Clear the CRC registers for our read of the + clrf M_CRCL ; MBR + movlw 0x22 ;Point the push pointer off into space and read + movwf FSR1H ; 446 bytes to get us past the bootstrap area; + movlw 0 ; we want to update the CRC registers with this + call MmcReadData ; data but we don't use it for anything + movlw 190 ; " + call MmcReadData ; " + movlw 0x21 ;Read the remaining 66 bytes of the MBR (the + movwf FSR0H ; partition table and the 0x55 0xAA signature, + movwf FSR1H ; hopefully) into memory + movlw 0x80 ; " + movwf FSR0L ; " + movwf FSR1L ; " + movlw 66 ; " + call MmcReadData ; " + call MmcCheckCrc ;Check the CRC of the data read against what + btfsc STATUS,Z ; we calculated; if they don't match, try + bra SysIni1 ; again, if we already tried four times, give + decfsz X2,F ; up and pass a code to the error handler + bra SysIni0 ; " + movlw 12 ; " + bra GoError ; " +SysIni1 bsf CS_PORT,CS_PIN ;Deassert !CS + moviw -2[FSR1] ;Check that the MBR signature matches, if it + xorlw 0x55 ; doesn't, this isn't an MBR and we can't do + movlw 13 ; anything, so pass a code to the error handler + btfss STATUS,Z ; " + bra GoError ; " + moviw -1[FSR1] ; " + xorlw 0xAA ; " + movlw 13 ; " + btfss STATUS,Z ; " + bra GoError ; " + movlw 4 ;Iterate through the four primary MBR + movwf X3 ; partitions +SysIni2 movlw 4 ;Try loading the block four times which is, + movwf X2 ; frankly, excessive +SysIni3 bsf CS_PORT,CS_PIN ;Deassert !CS + movlw 1 ;Set the first byte of the partition entry as + movwf INDF0 ; a flag for later use + moviw 4[FSR0] ;Interpret the type field of this partition as + movwf M_ADR0 ; a block number and set up to read that block + btfsc STATUS,Z ;If it's zero, skip doing this + bra SysIni4 ; " + xorlw 0xAF ;If it's 0xAF (HFS partition), skip doing this + btfsc STATUS,Z ; " + bra SysIni4 ; " + movlw 0x51 ;Set up a read command for the card (R1-type + movwf M_CMDN ; response) + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + clrf M_ADR3 ;The rest of the address is all zeroes + clrf M_ADR2 ; " + clrf M_ADR1 ; " + call MmcConvAddr ;Convert address to bytes if necessary + bcf CS_PORT,CS_PIN ;Assert !CS + call MmcCmd ;Send the read command + movf M_CMDN,W ;Treat any error flag as a failed command + andlw B'11111110' ; " + btfss STATUS,Z ; " + bsf M_FLAGS,M_FAIL ; " + movlw 14 ;If the operation failed, give up and pass a + btfsc M_FLAGS,M_FAIL ; code to the error handler + bra GoError ; " + call MmcGetData ;Get the card ready to read out data + movlw 15 ;If the operation failed, give up and pass a + btfsc M_FLAGS,M_FAIL ; code to the error handler + bra GoError ; " + clrf M_CRCH ;Clear the CRC registers for our read of the + clrf M_CRCL ; icon block + movlw 0x22 ;Point the push pointer off into space and read + movwf FSR1H ; 256 bytes to read what we hope is the word + movlw 0 ; 'ICON' repeated 64 times and check this by + call MmcReadData ; means of checking that the CRC is 0xBEF6 + movf M_CRCH,W ;If the CRC doesn't match, clear that flag we + xorlw 0xBE ; set earlier + btfss STATUS,Z ; " + clrf INDF0 ; " + movf M_CRCL,W ; " + xorlw 0xF6 ; " + btfss STATUS,Z ; " + clrf INDF0 ; " + movlw 0 ;Read the rest of the block and throw it away + call MmcReadData ; " + call MmcCheckCrc ;Check the CRC of the data read against what + btfsc STATUS,Z ; we calculated; if they don't match, try + bra SysIni4 ; again, if we already tried four times, give + decfsz X2,F ; up and pass a code to the error handler + bra SysIni3 ; " + movlw 16 ; " + bra GoError ; " +SysIni4 bsf CS_PORT,CS_PIN ;Deassert !CS + movlw 0 ;If the CRC of the icon block didn't match, + btfss INDF0,0 ; clear the partition type to zero so we don't + movwi 4[FSR0] ; present it as a drive + addfsr FSR0,16 ;Move on to the next partition until we've + decfsz X3,F ; done all four + bra SysIni2 ; " + movlw 0x21 ;Point both pointers at the partition table in + movwf FSR0H ; memory + movwf FSR1H ; " + movlw 0x80 ; " + movwf FSR0L ; " + movwf FSR1L ; " + movlw 4 ;Iterate through all four partition entries + movwf X0 ; " + movlw -1 ;Start off with zero devices and increment from + movwf DEVMAX ; there +SysIni5 moviw 4[FSR0] ;If this partition's type is zero or was made + btfsc STATUS,Z ; zero because it pointed to an invalid icon, + bra SysIni6 ; skip over it + moviw 15[FSR0] ;If this partition's size is greater than + btfss STATUS,Z ; 0x7FFFFF blocks, skip over it + bra SysIni6 ; " + moviw 14[FSR0] ; " + andlw B'10000000' ; " + btfss STATUS,Z ; " + bra SysIni6 ; " + moviw 8[FSR0] ;Copy the starting block number and block + movwi 3[FSR1] ; count from the MBR partition entry into a + moviw 9[FSR0] ; smaller partition entry for our use + movwi 2[FSR1] ; " + moviw 10[FSR0] ; " + movwi 1[FSR1] ; " + moviw 11[FSR0] ; " + movwi 0[FSR1] ; " + moviw 12[FSR0] ; " + movwi 7[FSR1] ; " + moviw 13[FSR0] ; " + movwi 6[FSR1] ; " + moviw 14[FSR0] ; " + movwi 5[FSR1] ; " + moviw 4[FSR0] ; " + movwi 4[FSR1] ; " + incf DEVMAX,F ;Increment the number of valid devices + addfsr FSR1,8 ;Move push pointer ahead to next entry +SysIni6 addfsr FSR0,16 ;Move pop pointer ahead to next entry + decfsz X0,F ;Decrement entry count and go to process the + bra SysIni5 ; next if applicable + incf DEVMAX,W ;If there are no valid partitions, that's an + movlw 17 ; error too + btfsc STATUS,Z ; " + bra GoError ; " + bsf INTCON,GIE ;Setup is all done, enable interrupts and + movlp 0 ; wait for some drive activity to respond to + goto Unselected ; " + + +;;; Receiver ;;; + +Receiver + movlb 3 ;Assert !HSHK to signal to Mac that we're + btfss TXSTA,TRMT ; ready to receive + bra $-1 ; " + movlw 0x80 ; " + movwf TXREG ; " + movlb 7 ;Set the IOC peripheral up as required by the + movlw B'00000100' ; interrupt handler, catching rising edges on + movwf IOCAP ; PH1 (signalling the end of a transmission) + clrf IOCAN ; and both rising and falling edges on WR (to + movlw B'00100000' ; catch the start of a new byte) + movwf IOCCP ; " + movwf IOCCN ; " + clrf IOCAF ; " + clrf IOCCF ; " + movlb 0 ; " + movlw 0x21 ;Move pointers to the beginning of the queue + movwf FSR0H ; in linear memory from 0x2100-0x217F + movwf FSR1H ; " + clrf FSR0L ; " + clrf FSR1L ; " + clrf IWMFSAP ;Point IWM FSA pointer at starting state + clrf IWMFLAG ;Clear IWM flags before we start interrupts + bsf INTCON,TMR0IE ;Enable interrupt on Timer0, starting receive + ;fall through + +WaitForCmd + movlw 0x05 ;Check if we've gotten five bytes yet, which is + subwf FSR1L,W ; most of the header of a command, enough to + btfsc STATUS,C ; tell what it is; if we have, skip ahead to + goto ProcessCmd ; process it + btfss IWMFLAG,IWMOVER ;If the transmission hasn't ended yet, loop + bra WaitForCmd ; until it does or it reaches five bytes + call ReturnToIdle ;If the transmission ended before it reached + movlp 0 ; five bytes, wait until Mac returns to the + goto IdleLoop ; idle state and go back to the idle loop + +ProcessCmd + clrf CHKSUM ;Start checksum off at zero + clrf RESPERR ;Response error is zero by default + moviw FSR0++ ;Store the first five bytes of the command + movwf CMDNUM ; outside the queue so they don't get + subwf CHKSUM,F ; overwritten, updating the checksum as we go + moviw FSR0++ ; " + movwf CMDCNT ; " + subwf CHKSUM,F ; " + moviw FSR0++ ; " + movwf CMDHIGH ; " + subwf CHKSUM,F ; " + moviw FSR0++ ; " + movwf CMDMED ; " + subwf CHKSUM,F ; " + moviw FSR0++ ; " + movwf CMDLOW ; " + subwf CHKSUM,F ; " + decf CMDNUM,W ;If command byte is 1 or 65 (write or continued + btfsc STATUS,Z ; write) we can't afford to wait for the end of + goto CmdWrite ; the command, so start processing immediately; + addlw -64 ; fall through to wait for other commands to + btfsc STATUS,Z ; finish before responding to them + goto CmdWriteCont ; " + ;fall through + +WaitToFinish + btfsc IWMFLAG,IWMOVER ;Check if the last byte's been received and + bra WaitFn0 ; skip ahead if it has + movf FSR0L,W ;If we haven't yet received a byte, loop until + xorwf FSR1L,W ; we have + btfsc STATUS,Z ; " + bra WaitToFinish ; " + moviw FSR0++ ;Update the checksum with the byte and loop + bcf FSR0L,7 ; again - besides writes, which don't use this + subwf CHKSUM,F ; loop, all the commands we care about have + bra WaitToFinish ; the important info in the first five bytes +WaitFn0 movf FSR0L,W ;If we've updated the checksum with all the + xorwf FSR1L,W ; bytes in this command, skip ahead + btfsc STATUS,Z ; " + goto ReceiveDone ; " + moviw FSR0++ ;Update the checksum with this byte and loop + bcf FSR0L,7 ; to process the next, if there is one + subwf CHKSUM,F ; " + bra WaitFn0 ; " + +ReceiveDone + call ReturnToIdle ;Return to the idle state (or the idle loop) + movf CHKSUM,W ;If checksum didn't match the data (that is, + btfss STATUS,Z ; make it sum to zero), we send a NAK so + goto BadChecksum ; hopefully Mac will send the command again + movf CMDNUM,W ;If the command number is 0, this is a read + btfsc STATUS,Z ; command + goto CmdRead ; " + addlw -3 ;If the command number is 3, this is a status + btfsc STATUS,Z ; command; if it's anything else, we don't know + goto CmdStatus ; how to handle it, so fake a response + clrf CMDCNT ; " + ;fall through + +CmdPlaceholder + call RequestToSend ;Assert !HSHK and wait to be in state 1; if + btfss STATUS,Z ; we ended up in an unexpected state, bail out + bra CmdPla3 ; to idle loop + movlw 0x80 ;Initialize the state used by the WriteByte + movwf IWMLSBS ; subprogram + clrf CHKSUM ; " + movlw 0xAA ;Send the sync byte + call WriteIwmByte ; " + movf CMDNUM,W ;Form a response code from the command number + andlw B'00111111' ; by setting the MSB (and clearing byte 6, + iorlw B'10000000' ; which seems to indicate a continued command) + call WriteByte ; and send that byte + movf CMDCNT,W ;Write byte count, command error byte (in case + call WriteByte ; this routine is being used for an error + movf RESPERR,W ; response), and pad bytes + call WriteByte ; " + movlw 0 ; " + call WriteByte ; " + movlw 0 ; " + call WriteByte ; " + movlw 0 ; " + call WriteByte ; " + decf IWMREXP,F ;If the expected response length was only one + btfsc STATUS,Z ; group, skip ahead to send the checksum and + bra CmdPla2 ; finish +CmdPla0 movlw 7 ;Pad out the response with groups of zeroes + movwf X0 ; until it reaches the size the command called +CmdPla1 movlw 0 ; for + call WriteByte ; " + decfsz X0,F ; " + bra CmdPla1 ; " + decfsz IWMREXP,F ; " + bra CmdPla0 ; " +CmdPla2 movf CHKSUM,W ;Write the checksum to finish out the last + call WriteByte ; group + call ReturnToIdle ;Deassert !HSHK and wait to be in state 2 +CmdPla3 movlp 0 ;Return to the idle loop + goto IdleLoop ; " + +BadChecksum + call RequestToSend ;Assert !HSHK and wait to be in state 1; if + btfss STATUS,Z ; we ended up in an unexpected state, bail out + bra BadChk1 ; to idle loop + movlw 0x80 ;Initialize the state used by the WriteByte + movwf IWMLSBS ; subprogram + clrf CHKSUM ; " + movlw 0xAA ;Send the sync byte + call WriteIwmByte ; " + movlw 0x7F ;Send the response code 0x7F, which is used to + call WriteByte ; mean NAK, a prompt for Mac to resend + movlw 5 ;Pad out the rest of the group + movwf X0 ; " +BadChk0 movlw 0 ; " + call WriteByte ; " + decfsz X0,F ; " + bra BadChk0 ; " + movf CHKSUM,W ;Write the checksum to finish out the group + call WriteByte ; " + call ReturnToIdle ;Deassert !HSHK and wait to be in state 2 +BadChk1 movlp 0 ;Return to the idle loop + goto IdleLoop ; " + +CmdRead + call MmcStopOngoing ;Make sure any ongoing write is stopped + movlw ERRCODE ;Ready an error code in case we need to jump + movwf RESPERR ; into the placeholder response routine + call SetupCmdAddr ;Set up the card address for this command + btfsc STATUS,C ;If the read is out of bounds, send an error + goto CmdPlaceholder ; response +CmdRea0 movlw 0x51 ;Set up a read command for the card (R1-type + movwf M_CMDN ; response) + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + bcf CS_PORT,CS_PIN ;Assert !CS + call MmcCmd ;Send the read command + movf M_CMDN,W ;Treat any error flag as a failed command + andlw B'11111110' ; " + btfss STATUS,Z ; " + bsf M_FLAGS,M_FAIL ; " + btfss M_FLAGS,M_FAIL ;If no failures up to this point, prepare the + call MmcGetData ; card to read out data + btfsc M_FLAGS,M_FAIL ;If there was any failure, respond with a + goto CmdPlaceholder ; placeholder with an error code + clrf M_CRCH ;Clear the CRC registers for our read of the + clrf M_CRCL ; block + movlw 0x21 ;Point both pointers to the 128-byte queue and + movwf FSR0H ; read the first two bytes of the sector out of + movwf FSR1H ; the card - we need to do this so we stay two + clrf FSR0L ; bytes ahead while writing the sector to the + clrf FSR1L ; USART so we know immediately if the CRC has + movlw 2 ; been matched or not and can mangle the + call MmcReadData ; checksum if it doesn't match + movlw 0x80 ;Initialize the state used by the WriteByte + movwf IWMLSBS ; subprogram + clrf CHKSUM ; " + call RequestToSend ;Assert !HSHK and wait to be in state 1; if + btfss STATUS,Z ; we ended up in an unexpected state, skip + clrf IWMLSBS ; writes so card stays in a good state + movlw 0xAA ;Send the sync byte + call WriteIwmByte ; " + movlw 0x80 ;Write the response code for a read command + call WriteByte ; " + movf CMDCNT,W ;Write out the count of remaining blocks to + call WriteByte ; send + movlw 24 ;Pad out the rest of the group and send 20 + movwf X0 ; empty tag bytes before sector data +CmdRea1 movlw 0 ; " + call WriteByte ; " + decfsz X0,F ; " + bra CmdRea1 ; " + movlw 2 ;Set up to copy 512 bytes from card to Mac + movwf X3 ; " + clrf X2 ; " +CmdRea2 moviw FSR0++ ;Pick up the next byte to write from the queue + call WriteByte ; and write it + movlw 1 ;Read a byte out of the card + call MmcReadData ; " + bcf FSR0L,7 ;Wrap both pointers in the queue + bcf FSR1L,7 ; " + decfsz X2,F ;Decrement the counter and loop if bytes remain + bra CmdRea2 ; " + decfsz X3,F ; " + bra CmdRea2 ; " + movf M_CRCH,W ;Feeding the CRC through the CRC registers + iorwf M_CRCL,W ; should leave it at zero if the CRC is right; + btfss STATUS,Z ; if it's not, send the wrong checksum so Mac + bra CmdRea4 ; knows something is wrong + movf CHKSUM,W ;Finish the block off with the (right) checksum + call WriteByte ; " + call MmcIncAddr ;Increment the block address for next time + call ReturnToIdle ;Return to the idle state + movf IWMLSBS,F ;If we got into an unexpected state somewhere, + btfsc STATUS,Z ; don't try to send the next block, just hurry + bra CmdRea3 ; back to the idle loop + decfsz CMDCNT,F ;Decrement the block count and go to send the + bra CmdRea0 ; next block if any are left +CmdRea3 bsf CS_PORT,CS_PIN ;Deassert !CS + movlp 0 ;Return to the idle loop + goto IdleLoop ; " +CmdRea4 comf CHKSUM,W ;CRC failed, so write what we know is the wrong + call WriteByte ; checksum to finish the transmission + call ReturnToIdle ;Return to the idle state quickly + bra CmdRea3 + +CmdStatus + call MmcStopOngoing ;Make sure any ongoing write is stopped + movlw 0x21 ;Point to the partition information block for + movwf FSR0H ; the selected device + swapf DEVNUM,W ; " + lsrf WREG,W ; " + iorlw B'10000000' ; " + movwf FSR0L ; " + moviw 4[FSR0] ;If the partition type is 0xAF, we're using a + xorlw 0xAF ; built-in icon, not one loaded from the card, + btfsc STATUS,Z ; so skip setting up the read + bra CmdSta0 ; " + movlw 0x51 ;Set up a read command for the card (R1-type + movwf M_CMDN ; response) + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + clrf M_ADR3 ;Point to the address of the icon to be loaded + clrf M_ADR2 ; " + clrf M_ADR1 ; " + moviw 4[FSR0] ; " + movwf M_ADR0 ; " + bcf CS_PORT,CS_PIN ;Assert !CS + call MmcConvAddr ;Convert address to bytes if necessary + btfss M_FLAGS,M_FAIL ;If the address conversion didn't fail, send + call MmcCmd ; the read command + movf M_CMDN,W ;Treat any error flag as a failed command + andlw B'11111110' ; " + btfss STATUS,Z ; " + bsf M_FLAGS,M_FAIL ; " + btfsc M_FLAGS,M_FAIL ;If the operation failed, use the built-in icon + bra CmdSta0 ; instead + call MmcGetData ;Get the card ready to read out data + movlw 0x22 ;Point the push pointer off into space and + movwf FSR1H ; read and throw away the first 256 bytes of + movlw 0 ; the icon sector + call MmcReadData ; " +CmdSta0 movlw 0x80 ;Initialize the state used by the WriteByte + movwf IWMLSBS ; subprogram + clrf CHKSUM ; " + call RequestToSend ;Assert !HSHK and wait to be in state 1; if + btfss STATUS,Z ; we ended up in an unexpected state, skip + clrf IWMLSBS ; writes so card stays in a good state + movlw 0xAA ;Send the sync byte + call WriteIwmByte ; " + movlw 0x83 ;Write the response code for a status command + call WriteByte ; " + movlw B'00000101' ;Write out nine bytes that happen to all be + movwf X0 ; 0x01 or 0x00 - pad byte, status byte, three + bcf STATUS,C ; pad bytes, device type word (0x0001), device +CmdSta1 movlw 0 ; manufacturer word (0x0001) + rlf WREG,W ; " + call WriteByte ; " + movf X0,W ; " + btfsc STATUS,Z ; " + bra CmdSta2 ; " + lslf X0,F ; " + bra CmdSta1 ; " +CmdSta2 movlw 0xE6 ;Write the device characteristics: mount, read, + call WriteByte ; write, icon included, disk in place + moviw 5[FSR0] ;Write the device size which I BELIEVE is + movwf X2 ; really the max sector number (so we have to + moviw 6[FSR0] ; decrement from the partition table) + movwf X1 ; TODO look into this and make sure + moviw 7[FSR0] ; " + movwf X0 ; " + movlw 0xFF ; " + addwf X0,F ; " + addwfc X1,F ; " + addwfc X2,W ; " + call WriteByte ; " + movf X1,W ; " + call WriteByte ; " + movf X0,W ; " + call WriteByte ; " + movlw 56 ;Write the spare count word, the bad block + movwf X0 ; count word, and the manufacturer reserved +CmdSta3 movlw 0 ; area (52 bytes), all as zeroes + call WriteByte ; " + decfsz X0,F ; " + bra CmdSta3 ; " + clrf X2 ;Clear the did-we-use-the-built-in-icon flag + moviw 4[FSR0] ;If the partition type is 0xAF or we had a fail + xorlw 0xAF ; while reading from the card, we're using the + btfss STATUS,Z ; built-in icon, so skip ahead to do that + btfsc M_FLAGS,M_FAIL ; " + bra CmdSta4 ; " + movlw 0 ;Copy 256 bytes from the card to the Mac + call MmcCopyData ; " + bra CmdSta6 ;Skip ahead to finish the status block +CmdSta4 movlw high LutIcon|128;Copy the built-in icon from flash to the Mac + movwf FSR0H ; " + clrf FSR0L ; " + clrf X0 ; " +CmdSta5 moviw FSR0++ ; " + call WriteByte ; " + decfsz X0,F ; " + bra CmdSta5 ; " + bsf X2,0 ;Set the did-we-use-the-built-in-icon flag +CmdSta6 movlw 10 ;The credits + call WriteByte ; " + movlw 'T' ; " + call WriteByte ; " + movlw 'a' ; " + call WriteByte ; " + movlw 's' ; " + call WriteByte ; " + movlw 'h' ; " + call WriteByte ; " + movlw 'T' ; " + call WriteByte ; " + movlw 'w' ; " + call WriteByte ; " + movlw 'e' ; " + call WriteByte ; " + movlw 'n' ; " + call WriteByte ; " + movlw 't' ; " + call WriteByte ; " + movlw 'y' ; " + call WriteByte ; " + movlw 5 ;Write the spare count word, the bad block + movwf X0 ; count word, and the manufacturer reserved +CmdSta7 movlw 0 ; area (52 bytes), all as zeroes + call WriteByte ; " + decfsz X0,F ; " + bra CmdSta7 ; " + movf CHKSUM,W ;Finish the block off with the checksum + call WriteByte ; " + btfss X2,0 ;If we didn't use the built-in icon, finish up + call MmcCheckCrc ; with the card by ignoring the CRC + bsf CS_PORT,CS_PIN ;Deassert !CS + call ReturnToIdle ;Return to the idle state + movlp 0 ;Return to the idle loop + goto IdleLoop ; " + +CmdWrite + movlw ERRCODE ;Ready an error code in case we need to jump + movwf RESPERR ; into the placeholder response routine + call SetupCmdAddr ;Set up the card address for this command + btfsc STATUS,C ;If the write is out of bounds, send an error + goto WaitToFinish ; response after we've received the command + btfsc M_FLAGS,M_MBWR ;If we're starting a new write even though a + bra CmdWri7 ; multiblock write is ongoing, handle it + movlw 0x59 ;Set up a multiblock write command for the card + movwf M_CMDN ; (R1-type reply) + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + bcf CS_PORT,CS_PIN ;Assert !CS + call MmcCmd ;Send the command + movf M_CMDN,W ;Treat any error flag as a failed command + andlw B'11111110' ; " + btfss STATUS,Z ; " + bsf M_FLAGS,M_FAIL ; " + btfsc M_FLAGS,M_FAIL ;If there was any failure, respond with a + goto WaitToFinish ; placeholder with an error code + bsf M_FLAGS,M_MBWR ;Flag that a multiblock write is in progress +CmdWri0 movlb 4 ;Clock a dummy byte while keeping MOSI high + movlw 0xFF ; " + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movlw 0xFC ;Clock the multiblock data token into the card + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movlb 0 ; " + clrf M_CRCH ;Clear the CRC registers for our write of the + clrf M_CRCL ; block + movlw 21 ;Skip over the command pad byte plus the 20 tag + movwf X0 ; bytes +CmdWri1 btfsc IWMFLAG,IWMOVER ;It shouldn't happen, but we do need to be + bra CmdWri2 ; ready in case transmission ends prematurely + movf FSR0L,W ;If we haven't yet received a byte, loop until + xorwf FSR1L,W ; we have + btfsc STATUS,Z ; " + bra CmdWri1 ; " + moviw FSR0++ ;Update the checksum with the byte and loop + bcf FSR0L,7 ; again until we've done all the necessary + subwf CHKSUM,F ; byte-skipping +CmdWri2 decfsz X0,F ; " + bra CmdWri1 ; " + movlw 2 ;Copy 512 data bytes from Mac + movwf X1 ; " +CmdWri3 btfsc IWMFLAG,IWMOVER ;It shouldn't happen, but we do need to be + bra CmdWri4 ; ready in case transmission ends prematurely + movf FSR0L,W ;If we haven't yet received a byte, loop until + xorwf FSR1L,W ; we have + btfsc STATUS,Z ; " + bra CmdWri3 ; " + moviw FSR0++ ;Grab the byte from the queue and wrap the + bcf FSR0L,7 ; pointer + subwf CHKSUM,F ;Update the checksum +CmdWri4 movlb 4 ;Clock the byte out to the card + movwf SSP1BUF ; " + xorwf M_CRCH,W ;Update the CRC with the byte while it's + movwf M_CRCH ; clocking out + movlp high LutCrc16H ; " + callw ; " + xorwf M_CRCL,W ; " + xorwf M_CRCH,F ; " + xorwf M_CRCH,W ; " + xorwf M_CRCH,F ; " + movlp high LutCrc16L ; " + callw ; " + movwf M_CRCL ; " + movlp 8 ; " + btfss SSP1STAT,BF ;Wait until the byte is done transmitting + bra $-1 ; " + movlb 0 ; " + decfsz X0,F ;Loop again until we've copied 512 bytes + bra CmdWri3 ; " + decfsz X1,F ; " + bra CmdWri3 ; " + ;TODO wreck the CRC in case the transmission ended prematurely + btfss IWMFLAG,IWMOVER ;Wait for the transmission to be over + bra $-1 ; " + moviw FSR0++ ;Grab the last byte from the queue and wrap the + bcf FSR0L,7 ; pointer + subwf CHKSUM,F ;Update the checksum + btfss STATUS,Z ;If the checksum is not zero, wreck the CRC so + comf M_CRCL,F ; the card doesn't store the bad data + movlb 4 ;Clock the high byte of the CRC out to the card + movf M_CRCH,W ; " + movwf SSP1BUF ; " + btfss SSP1STAT,BF ;Wait until the byte is done transmitting + bra $-1 ; " + movf M_CRCL,W ;Clock the low byte of the CRC out to the card + movwf SSP1BUF ; " + btfss SSP1STAT,BF ;Wait until the byte is done transmitting + bra $-1 ; " + movlw 0xFF ;Clock data response byte out of the MMC card + movwf SSP1BUF ; while keeping MOSI high + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf SSP1BUF,W ;Save data response to check later + movwf X2 ; " + call MmcWaitBusy ;Wait for the card not to be busy anymore + movlb 0 ;If the data response byte indicated that the + movf X2,W ; write succeeded, skip ahead + andlw B'00011111' ; " + xorlw B'00000101' ; " + btfsc STATUS,Z ; " + bra CmdWri6 ; " +CmdWri5 call MmcStopOngoing ;Stop the ongoing write + btfss IWMFLAG,IWMOVER ;Wait until the Mac's transmission is over + bra $-1 ; " + call ReturnToIdle ;If the checksum was wrong, send a NAK, if it + movf CHKSUM,W ; was right, send an error + btfss STATUS,Z ; " + goto BadChecksum ; " + goto CmdPlaceholder ; " +CmdWri6 decf CMDCNT,W ;If this was the last block in the write, stop + btfsc STATUS,Z ; the ongoing write + call MmcStopOngoing ; " + clrf RESPERR ;Send a success response + call ReturnToIdle ; " + goto CmdPlaceholder ; " +CmdWri7 movlw 1 ;In the unlikely event Mac tried to start a new + movwf CHKSUM ; write while one was ongoing, pretend there + bra CmdWri5 ; was a bad checksum so it tries again + +CmdWriteCont + movlw ERRCODE ;Ready an error code in case we need to jump + movwf RESPERR ; into the placeholder response routine + btfsc M_FLAGS,M_MBWR ;If a multibyte write is already ongoing, as it + bra CmdWri0 ; should be, jump into the middle of CmdWrite, + goto WaitToFinish ; otherwise send an error response + + +;;; Subprograms ;;; + +;Use the address and block count from the received command along with the +; partition start block to to populate the card address registers, also +; checking whether the address and block count stay within the partition. +; Returns with carry bit set on failure, clear on success. Trashes X0 and X1. +SetupCmdAddr + movf FSR0H,W ;Save FSR0 because we're going to use it for + movwf X1 ; looking up the partition information + movf FSR0L,W ; " + movwf X0 ; " + movlw 0x21 ;Point to the partition information block for + movwf FSR0H ; the selected device + swapf DEVNUM,W ; " + lsrf WREG,W ; " + iorlw B'10000000' ; " + movwf FSR0L ; " + decf CMDCNT,W ;Reckon the maximum block number accessed by + addwf CMDLOW,W ; this command + movwf M_ADR0 ; " + movlw 0 ; " + addwfc CMDMED,W ; " + movwf M_ADR1 ; " + movlw 0 ; " + addwfc CMDHIGH,W ; " + movwf M_ADR2 ; " + moviw 5[FSR0] ;If the partition size is less than or equal to + subwf M_ADR0,W ; the maximum block number accessed, return + moviw 6[FSR0] ; with carry set + subwfb M_ADR1,W ; " + moviw 7[FSR0] ; " + subwfb M_ADR2,W ; " + btfsc STATUS,C ; " + bra SetCmA0 ; " + movf INDF0,W ;Move the starting block of the partition into + movwf M_ADR3 ; the card address registers + moviw 1[INDF0] ; " + movwf M_ADR2 ; " + moviw 2[INDF0] ; " + movwf M_ADR1 ; " + moviw 3[INDF0] ; " + addwf CMDLOW,W ;Add the block address from the command to it + movwf M_ADR0 ; " + movf CMDMED,W ; " + addwfc M_ADR1,F ; " + movf CMDHIGH,W ; " + addwfc M_ADR2,F ; " + movlw 0 ; " + addwfc M_ADR3,F ; " + call MmcConvAddr ;Convert address to bytes if necessary + bcf STATUS,C ;Make sure carry is clear before returning + btfsc M_FLAGS,M_FAIL ;But set carry if the address conversion failed + bsf STATUS,C ; " +SetCmA0 movf X1,W ;Restore FSR0 + movwf FSR0H ; " + movf X0,W ; " + movwf FSR0L ; " + return + +;Write the byte in W to the Mac, taking care of LSBs and holdoff. Note that +; IWMLSBS should be set to 0x80 and CHKSUM should be set to 0 before calling +; this function for the first time in a transmission. +WriteByte + movf IWMLSBS,F ;If the command has been aborted, return + btfsc STATUS,Z ; without sending anything, hopefully to finish + return ; up quickly + subwf CHKSUM,F ;Adjust checksum with the byte to be written + bsf STATUS,C ;Shift out the byte's LSB, shifting a 1 into + rrf WREG,W ; the MSB and catching the LSB in the LSBs + rrf IWMLSBS,F ; shift register + call WriteIwmByte ;Write the IWM byte out on the USART + btfss IWMLSBS,0 ;If the LSB shift register isn't full yet, + return ; we're done here + bsf STATUS,C ;Shift a 1 into the MSB of the LSB shift + rrf IWMLSBS,W ; register and write that out on the USART + call WriteIwmByte ; " + movlw 0x80 ;Prepare the LSB shift register for next group + movwf IWMLSBS ; " +WriteB0 lsrf PORTA,W ;Take a quick look at the current state + xorlw B'00000001' ;If we're still in state 1 (data transfer), all + btfsc STATUS,Z ; is normal, return to the caller + return ; " +WriteB1 lsrf PORTA,W ;Take another quick look at the current state + btfsc STATUS,Z ;If we're in the holdoff state, spin until + bra WriteB1 ; we aren't + xorlw B'00000001' ;If the state we're in is state 1 (data + btfsc STATUS,Z ; transfer), we've exited holdoff and must send + bra WriteB2 ; a sync byte before resuming + movlb 3 ;If we're in some other state, the command has + clrf TXREG ; been aborted or something is awry, but we may + movlb 0 ; be in the middle of something that has to + clrf IWMLSBS ; finish, so set RD and clear the LSBs SR as an + return ; indicator that future calls exit immediately +WriteB2 movlw 0xAA ;Write a sync byte over the USART to signal + call WriteIwmByte ; that we're resuming transmission + return + +;Write the raw IWM byte in W to the USART. +WriteIwmByte + movf IWMLSBS,F ;If the command has been aborted, return + btfsc STATUS,Z ; without sending anything, hopefully to finish + return ; up quickly + movlp high LutFlip ;Use the LUT to flip the byte left to right so + callw ; it goes out correctly on the USART + btfss PIR1,TXIF ;Wait until the USART is ready to accept a byte + bra $-1 ; " + movlb 3 ;Write the byte to the USART + movwf TXREG ; " + movlb 0 ; " + movlp 8 ;Restore PCLATH + return + +;Wait until Mac returns to the idle state (PH2-0 = 2), allowing it to make its +; way through states 0 (holdoff), 1 (data), and 3 (data request), but bailing +; out to the idle loop if it enters any other state, if PH3 is high, or if +; !ENBL is high. +ReturnToIdle + btfss PIR1,TXIF ;In case we're coming here from post-transmit, + bra $-1 ; make sure we don't stomp a waiting byte + movlb 3 ;Raise !HSHK to confirm to Mac that we're + clrf TXREG ; ready to return to idle state + movlb 31 ;Prepare for a possible return to the idle loop + decf STKPTR,F ; by (possibly temporarily) swallowing the + movlb 0 ; return address and pointing PCLATH to the + movlp 0 ; first half of program memory +RetIdl0 lsrf PORTA,W ;Switch based on current state + brw ; " + bra RetIdl0 ;In holdoff state (why?), keep checking + bra RetIdl0 ;Still in data state, keep checking + bra RetIdl1 ;In idle state, we're done here + bra RetIdl0 ;Transitioning through negotiation state + goto IdleLoop ;Anything else is cause to bail out + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto IdleLoop ; " + goto Unselected ;!ENBL has been deasserted, bail WAY out + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " + goto Unselected ; " +RetIdl1 movlb 31 ;We've successfully transitioned to the idle + incf STKPTR,F ; state, so restore the stack pointer and + movlb 0 ; PCLATH and return to the caller + movlp 8 ; " + return ; " + +RequestToSend + movlb 3 ;Assert !HSHK to tell Mac we want to send some + btfss TXSTA,TRMT ; data + bra $-1 ; " + movlw 0x80 ; " + movwf TXREG ; " + movlb 0 ; " +ReqSnd0 lsrf PORTA,W ;Switch based on current state + brw ; " + bra ReqSnd0 ;In holdoff state (why?), keep checking + bra ReqSnd1 ;In data state, we're done here + bra ReqSnd0 ;In idle state, keep checking + bra ReqSnd0 ;Transitioning through negotiation state + return ;Anything else is cause to bail out with Z + return ; clear, indicating to caller that they should + return ; get back to the idle loop, stat + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " + return ; " +ReqSnd1 bsf STATUS,Z ;We've successfully requested to send, set the + return ; Z bit and return + + +;;; MMC Subprograms ;;; + +;Initialize MMC card. Sets M_FAIL on fail. Trashes X0 through X3. +MmcInit + clrf M_FLAGS ;Make sure flags are all clear to begin with + call MmcIni0 ;Call into the function below + bsf CS_PORT,CS_PIN ;Always deassert !CS + movf WREG,W ;If the init function returned a code other + btfss STATUS,Z ; than 0, set the fail flag + bsf M_FLAGS,M_FAIL ; " + return ;Pass return code to caller +MmcIni0 movlb 4 ;This is where all the SSP registers are + movlw 10 ;Send 80 clocks on SPI interface to ensure MMC + movwf X0 ; card is started up and in native command mode +MmcIni1 movlw 0xFF ; " + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + decfsz X0,F ; " + bra MmcIni1 ; " + movlb 0 ;Assert !CS + bcf CS_PORT,CS_PIN ; " + movlw 0x40 ;Send command 0 (expect R1-type response) + movwf M_CMDN ; which, with !CS asserted, signals to the card + clrf M_ADR3 ; that we want to enter SPI mode + clrf M_ADR2 ; " + clrf M_ADR1 ; " + clrf M_ADR0 ; " + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + btfsc M_FLAGS,M_FAIL ;If this command failed, unrecognized or + retlw 1 ; missing MMC card, fail the init operation + movf M_CMDN,W ;If this command returned any response other + xorlw 0x01 ; than 0x01 ('in idle state'), unrecognized MMC + btfss STATUS,Z ; card, fail the init operation + retlw 2 ; " + bsf M_FLAGS,M_CDVER ;Assume version 2.0+ to begin with + clrf X2 ;Set retry counter to 0 (65536) for later use + clrf X3 ; " + movlw 0x48 ;Send command 8 (expect R7-type response) to + movwf M_CMDN ; check if we're dealing with a V2.0+ card + clrf M_ADR3 ; " + clrf M_ADR2 ; " + movlw 0x01 ; " + movwf M_ADR1 ; " + movlw 0xAA ; " + movwf M_ADR0 ; " + bsf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + movf M_CMDN,W ;If the command set any error flags or there + andlw B'11111110' ; was no response, switch assumptions and guess + btfsc STATUS,Z ; that we're dealing with a Version 1 card and + btfsc M_FLAGS,M_FAIL ; jump ahead to initialize it + bcf M_FLAGS,M_CDVER ; " + btfss M_FLAGS,M_CDVER ; " + bra MmcIni2 ; " + movf M_ADR1,W ;If the command didn't error, but the lower 12 + andlw B'00001111' ; bits of the R7 response are something besides + xorlw 0x01 ; 0x1AA, we're dealing with an unknown card, so + btfss STATUS,Z ; raise the fail flag and return to caller + retlw 3 ; " + movf M_ADR0,W ; " + xorlw 0xAA ; " + btfss STATUS,Z ; " + retlw 3 ; " +MmcIni2 movlw 0x77 ;Send command 55 (expect R1-type response), + movwf M_CMDN ; which is a prelude to an 'app' command + clrf M_ADR3 ; " + clrf M_ADR2 ; " + clrf M_ADR1 ; " + clrf M_ADR0 ; " + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + movf M_CMDN,W ;If we got a status with any error bits set, + andlw B'11111110' ; treat as a command failure + btfss STATUS,Z ; " + bsf M_FLAGS,M_FAIL ; " + btfsc M_FLAGS,M_FAIL ;If this command fails, this is an unknown card + retlw 4 ; so return the failure to caller + movlw 0x69 ;Send app command 41 (expect R1-type response) + movwf M_CMDN ; to initialize the card, setting the HCS + clrf M_ADR3 ; (high-capacity support) bit if we're dealing + btfsc M_FLAGS,M_CDVER ; with a V2.0+ card to let the card know that + bsf M_ADR3,6 ; we support cards bigger than 4 GB (up to 2 + clrf M_ADR2 ; TB) + clrf M_ADR1 ; " + clrf M_ADR0 ; " + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + movf M_CMDN,W ;If we got a status with any error bits set, + andlw B'11111110' ; treat as a command failure + btfss STATUS,Z ; " + bsf M_FLAGS,M_FAIL ; " + btfsc M_FLAGS,M_FAIL ;If this command fails, this is an unknown card + retlw 5 ; so return the failure to caller + btfss M_CMDN,0 ;If it returned an 0x00 status, initialization + bra MmcIni3 ; is finished + DELAY 40 ;If it returned an 0x01 status, delay for 120 + decfsz X2,F ; cycles (15 us), decrement the retry counter, + bra MmcIni2 ; and try again + decfsz X3,F ; " + bra MmcIni2 ; " + retlw 6 ;If card still not ready, report failure +MmcIni3 movlw 0x7A ;Send command 58 (expect R3-type response) to + movwf M_CMDN ; read the operating condition register (OCR) + clrf M_ADR3 ; " + clrf M_ADR2 ; " + clrf M_ADR1 ; " + clrf M_ADR0 ; " + bsf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + movf M_CMDN,W ;If we got a status with any error bits set, + btfss STATUS,Z ; treat as a command failure + bsf M_FLAGS,M_FAIL ; " + btfsc M_FLAGS,M_FAIL ;If this command fails, something is wrong, so + retlw 7 ; return the failure to caller + bsf M_FLAGS,M_BKADR ;If the card capacity status (CCS) bit of the + btfsc M_ADR3,6 ; OCR is set, we're using block addressing, so + bra MmcIni4 ; skip ahead + bcf M_FLAGS,M_BKADR ;We're dealing with byte, not block addressing + movlw 0x50 ;Send command 16 (expect R1-type response) to + movwf M_CMDN ; tell the card we want to deal in 512-byte + clrf M_ADR3 ; sectors + clrf M_ADR2 ; " + movlw 0x02 ; " + movwf M_ADR1 ; " + clrf M_ADR0 ; " + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + movf M_CMDN,W ;If this command returned any response other + btfss STATUS,Z ; than 0x00, something is wrong, fail the init + bsf M_FLAGS,M_FAIL ; operation + btfsc M_FLAGS,M_FAIL ;If this command failed, something is wrong, + retlw 8 ; fail the init operation +MmcIni4 movlw 0x7B ;Send command 59 (expect R1-type response) to + movwf M_CMDN ; tell the card we want to make life hard on + clrf M_ADR3 ; ourselves and have our CRCs checked by the + clrf M_ADR2 ; card + clrf M_ADR1 ; " + movlw 0x01 ; " + movwf M_ADR0 ; " + bcf M_FLAGS,M_CMDLR ; " + bcf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + movf M_CMDN,W ;If this command returned any response other + btfss STATUS,Z ; than 0x00, something is wrong, fail the init + bsf M_FLAGS,M_FAIL ; operation + btfsc M_FLAGS,M_FAIL ;If this command failed, something is wrong, + retlw 9 ; fail the init operation + retlw 0 ;Congratulations, card is initialized! + +;Convert a block address to a byte address if byte addressing is in effect. +; Sets M_FAIL if the block address is above 0x7FFFFF (and thus can't fit as a +; byte address). +MmcConvAddr + bcf M_FLAGS,M_FAIL ;Assume no failure to start with + btfsc M_FLAGS,M_BKADR ;If block addressing is in effect, the address + return ; does not need to be converted + movf M_ADR3,F ;Make sure that the top 9 bits of the block + btfss STATUS,Z ; address are clear; if they are not, set the + bsf M_FLAGS,M_FAIL ; fail flag + btfsc M_ADR2,7 ; " + bsf M_FLAGS,M_FAIL ; " + btfsc M_FLAGS,M_FAIL ;If the fail flag is set, we're done + return ; " + lslf M_ADR0,F ;Multiply the block address by 2 and then by + rlf M_ADR1,F ; 256 + rlf M_ADR2,W ; " + movwf M_ADR3 ; " + movf M_ADR1,W ; " + movwf M_ADR2 ; " + movf M_ADR0,W ; " + movwf M_ADR1 ; " + clrf M_ADR0 ; " + return + +;Increment the address by one block according to the block/byte addressing +; mode. No protection is provided against the address wrapping around. +MmcIncAddr + movlw 0x01 ;Add 1 to the address if we're in block mode, + btfss M_FLAGS,M_BKADR ; add 512 to the address if we're in byte mode, + movlw 0 ; in either case carrying the remainder through + addwf M_ADR0,F ; " + movlw 0 ; " + btfss M_FLAGS,M_BKADR ; " + movlw 0x02 ; " + addwfc M_ADR1,F ; " + movlw 0 ; " + addwfc M_ADR2,F ; " + addwfc M_ADR3,F ; " + return + +;Send the command contained in M_CMDN and M_ADR3-0 to MMC card. Sets M_FAIL on +; fail. Trashes X0 and X1. +MmcCmd + bcf M_FLAGS,M_FAIL ;Assume no failure to start with + clrf X0 ;Start the CRC7 register out at 0 + movlp high LutCrc7 ;Point PCLATH to the CRC7 lookup table + movlb 4 ;Switch to the bank with the SSP registers + movf M_CMDN,W ;Clock out all six MMC buffer bytes as command, + movwf SSP1BUF ; calculating the CRC7 along the way + xorwf X0,W ; " + callw ; " + movwf X0 ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf M_ADR3,W ; " + movwf SSP1BUF ; " + xorwf X0,W ; " + callw ; " + movwf X0 ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf M_ADR2,W ; " + movwf SSP1BUF ; " + xorwf X0,W ; " + callw ; " + movwf X0 ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf M_ADR1,W ; " + movwf SSP1BUF ; " + xorwf X0,W ; " + callw ; " + movwf X0 ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf M_ADR0,W ; " + movwf SSP1BUF ; " + xorwf X0,W ; " + callw ; " + movlp 8 ; " + movwf X0 ; " + bsf X0,0 ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf X0,W ; " + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + ;TODO for CMD12, it is necessary to clock and throw away a stuff byte? + movlw 8 ;Try to get status as many as eight times + movwf X0 ; " +MmcCmd1 movlw 0xFF ;Clock a byte out of the MMC card while keeping + movwf SSP1BUF ; MOSI high + btfss SSP1STAT,BF ; " + bra $-1 ; " + incf SSP1BUF,W ;If we read back anything but 0xFF, skip ahead + btfss STATUS,Z ; " + bra MmcCmd2 ; " + decfsz X0,F ;Decrement the attempt counter until we've + bra MmcCmd1 ; tried eight times; if we haven't gotten a + bsf M_FLAGS,M_FAIL ; reply by the eighth attempt, signal failure + movlb 0 ; and return + return ; " +MmcCmd2 decf WREG,W ;Store the byte we received as R1-type status + movwf M_CMDN ; in the buffer + btfss M_FLAGS,M_CMDLR ;If we aren't expecting a long (R3/R7-type) + bra MmcCmd3 ; reply, we're done + movlw 0xFF ;Clock first extended reply byte out of the + movwf SSP1BUF ; MMC card while keeping MOSI high and store it + btfss SSP1STAT,BF ; in the buffer + bra $-1 ; " + movf SSP1BUF,W ; " + movwf M_ADR3 ; " + movlw 0xFF ;Clock second extended reply byte out of the + movwf SSP1BUF ; MMC card while keeping MOSI high and store it + btfss SSP1STAT,BF ; in the buffer + bra $-1 ; " + movf SSP1BUF,W ; " + movwf M_ADR2 ; " + movlw 0xFF ;Clock third extended reply byte out of the + movwf SSP1BUF ; MMC card while keeping MOSI high and store it + btfss SSP1STAT,BF ; in the buffer + bra $-1 ; " + movf SSP1BUF,W ; " + movwf M_ADR1 ; " + movlw 0xFF ;Clock fourth extended reply byte out of the + movwf SSP1BUF ; MMC card while keeping MOSI high and store it + btfss SSP1STAT,BF ; in the buffer + bra $-1 ; " + movf SSP1BUF,W ; " + movwf M_ADR0 ; " +MmcCmd3 btfsc M_FLAGS,M_CMDRB ;If we're expecting an R1b reply, wait for the + call MmcWaitBusy ; card not to be busy anymore before returning + movlb 0 ;Restore BSR to 0 + return + +;Waits for the card not to be busy anymore. Sets M_FAIL on fail. Trashes X0 +; and X1. Expects BSR to be 4 and does not set or reset this. +MmcWaitBusy + clrf X0 ;Check 65536 times to see if the card is busy + clrf X1 ; " +MmcWai0 movlw 0xFF ;Clock a byte out of the MMC card while keeping + movwf SSP1BUF ; MOSI high + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf SSP1BUF,W ;Check if MISO is still low, if it's not, the + btfss STATUS,Z ; card is no longer busy and we can return + return ; " + decfsz X0,F ;If it's not done, try again + bra MmcWai0 ; " + decfsz X1,F ; " + bra MmcWai0 ; " + bsf M_FLAGS,M_FAIL ;If out of tries, fail the operation + return + +;Try to get the data token for a read from the card. Sets M_FAIL on fail. +; Trashes X0 and X1. +MmcGetData + bcf M_FLAGS,M_FAIL ;Assume no failure to start with + movlb 4 ;Switch to the bank with the SSP registers + clrf X0 ;Try 65536 times to get the data token + clrf X1 ; " +MmcGet0 movlw 0xFF ;Clock a byte out of the MMC card while keeping + movwf SSP1BUF ; MOSI high + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf SSP1BUF,W ;If we've received the data token, return + xorlw 0xFE ; " + btfsc STATUS,Z ; " + bra MmcGet1 ; " + decfsz X0,F ;If not, decrement the retry count and try + bra MmcGet0 ; again + decfsz X1,F ; " + bra MmcGet0 ; " + bsf M_FLAGS,M_FAIL ;If we didn't get the data token after 65536 +MmcGet1 movlb 0 ; tries, give up and fail the operation + return ; " + +;Read the number of bytes in W from the card into memory where FSR1 is pointed, +; updating CRC registers. Trashes X0 and X1. +MmcReadData + movwf X0 ;Store the count of bytes to read + movlb 4 ;Clock the first byte out of the card + movlw 0xFF ; " + movwf SSP1BUF ; " + btfss SSP1STAT,BF ;Wait until the transfer is done + bra $-1 ; " + movf SSP1BUF,W ;Store the received byte in the buffer and also + movwf X1 ; keep it to update the CRC with + movwi FSR1++ ; " + decf X0,F ;If for some reason the caller only wanted to + btfsc STATUS,Z ; read one byte, skip the next loop + bra MmcRea1 ; " +MmcRea0 movlw 0xFF ;Clock the next byte out of the card + movwf SSP1BUF ; " + movf X1,W ;Update the CRC with the previous byte while + xorwf M_CRCH,W ; the next one is clocking out of the card + movwf M_CRCH ; " + movlp high LutCrc16H ; " + callw ; " + xorwf M_CRCL,W ; " + xorwf M_CRCH,F ; " + xorwf M_CRCH,W ; " + xorwf M_CRCH,F ; " + movlp high LutCrc16L ; " + callw ; " + movwf M_CRCL ; " + btfss SSP1STAT,BF ;Wait until the transfer is done + bra $-1 ; " + movf SSP1BUF,W ;Store the received byte in the buffer and also + movwf X1 ; keep it to update the CRC with + movwi FSR1++ ; " + decfsz X0,F ;Decrement the byte counter and loop around if + bra MmcRea0 ; there are bytes left to clock out +MmcRea1 movf X1,W ;Update the CRC with the last byte + xorwf M_CRCH,W ; " + movwf M_CRCH ; " + movlp high LutCrc16H ; " + callw ; " + xorwf M_CRCL,W ; " + xorwf M_CRCH,F ; " + xorwf M_CRCH,W ; " + xorwf M_CRCH,F ; " + movlp high LutCrc16L ; " + callw ; " + movwf M_CRCL ; " + movlb 0 ;Restore BSR + movlp 8 ;Restore PCLATH + return + +;Read the number of bytes in W from the card into WriteByte, updating CRC +; registers. Trashes X0 and X1. +MmcCopyData + movwf X0 ;Store the count of bytes to read + movlb 4 ;Clock the first byte out of the card + movlw 0xFF ; " + movwf SSP1BUF ; " + btfss SSP1STAT,BF ;Wait until the transfer is done + bra $-1 ; " + movf SSP1BUF,W ;Store the received byte to update the CRC with + movwf X1 ; and also call WriteByte + movlb 0 ; " + call WriteByte ; " + movlb 4 ; " + decf X0,F ;If for some reason the caller only wanted to + btfsc STATUS,Z ; read one byte, skip the next loop + bra MmcCop1 ; " +MmcCop0 movlw 0xFF ;Clock the next byte out of the card + movwf SSP1BUF ; " + movf X1,W ;Update the CRC with the previous byte while + xorwf M_CRCH,W ; the next one is clocking out of the card + movwf M_CRCH ; " + movlp high LutCrc16H ; " + callw ; " + xorwf M_CRCL,W ; " + xorwf M_CRCH,F ; " + xorwf M_CRCH,W ; " + xorwf M_CRCH,F ; " + movlp high LutCrc16L ; " + callw ; " + movwf M_CRCL ; " + btfss SSP1STAT,BF ;Wait until the transfer is done + bra $-1 ; " + movf SSP1BUF,W ;Store the received byte to update the CRC with + movwf X1 ; and also call WriteByte + movlb 0 ; " + movlp 8 ; " + call WriteByte ; " + movlb 4 ; " + decfsz X0,F ;Decrement the byte counter and loop around if + bra MmcCop0 ; there are bytes left to clock out +MmcCop1 movf X1,W ;Update the CRC with the last byte + xorwf M_CRCH,W ; " + movwf M_CRCH ; " + movlp high LutCrc16H ; " + callw ; " + xorwf M_CRCL,W ; " + xorwf M_CRCH,F ; " + xorwf M_CRCH,W ; " + xorwf M_CRCH,F ; " + movlp high LutCrc16L ; " + callw ; " + movwf M_CRCL ; " + movlb 0 ;Restore BSR + movlp 8 ;Restore PCLATH + return + +;Read the CRC out of the card and check it against the calculated CRC. Sets Z +; if they match, clears it if not. +MmcCheckCrc + movlb 4 ;Clock the high CRC byte out of the card + movlw 0xFF ; " + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf SSP1BUF,W ; " + xorwf M_CRCH,W ;If it doesn't match what we calculated, we've + btfss STATUS,Z ; failed but still need to clock out the low + bra MmcChe0 ; byte so skip ahead + movlw 0xFF ;Clock the low CRC byte out of the card + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movf SSP1BUF,W ; " + xorwf M_CRCL,W ;The high byte matched, so whether the whole + movlb 0 ; thing matches or not hinges on the low byte, + return ; so compare them and return with result in Z +MmcChe0 movlw 0xFF ;Clock the low CRC byte out of the card + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + bcf STATUS,Z ;Clear Z because high byte didn't match and + movlb 0 ; return + return ; " + +;Stop a multiblock read or write, if one is ongoing, and deassert !CS. Sets +; M_FAIL on fail. Trashes X0 and X1. +MmcStopOngoing + bcf M_FLAGS,M_FAIL ;Assume no failure to start with + btfsc M_FLAGS,M_MBWR ;If there's an ongoing write operation, make it + bra MmcSto0 ; stop + btfss M_FLAGS,M_MBRD ;If there's not an ongoing read operation, this + return ; function was called needlessly, return + movlw 0x4C ;Send a CMD12 to stop the ongoing read, R1b + movwf M_CMDN ; reply type + clrf M_ADR3 ; " + clrf M_ADR2 ; " + clrf M_ADR1 ; " + clrf M_ADR0 ; " + bcf M_FLAGS,M_CMDLR ; " + bsf M_FLAGS,M_CMDRB ; " + call MmcCmd ; " + bcf M_FLAGS,M_MBRD ;Clear the ongoing multiblock read flag + bsf CS_PORT,CS_PIN ;Deassert !CS + return +MmcSto0 movlb 4 ;Switch to the bank with the SSP registers + movlw 0xFD ;Clock the end-of-data token into the MMC card + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + movlw 0xFF ;Clock a dummy byte while keeping MOSI high + movwf SSP1BUF ; " + btfss SSP1STAT,BF ; " + bra $-1 ; " + call MmcWaitBusy ;Wait for the card to not be busy anymore + movlb 0 ;Return to the default bank + bcf M_FLAGS,M_MBWR ;Clear the ongoing multiblock write flag + bsf CS_PORT,CS_PIN ;Deassert !CS + return + + +;;; End of Program ;;; + + end diff --git a/firmware1704/firmware1704.mcp b/firmware1704/firmware1704.mcp new file mode 100644 index 0000000..9cf8d2e --- /dev/null +++ b/firmware1704/firmware1704.mcp @@ -0,0 +1,77 @@ +[HEADER] +magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13} +file_version=1.0 +device=PIC16F1704 +[PATH_INFO] +BuildDirPolicy=BuildDirIsProjectDir +dir_src= +dir_bin= +dir_tmp= +dir_sin= +dir_inc= +dir_lib= +dir_lkr= +[CAT_FILTERS] +filter_src=*.asm +filter_inc=*.h;*.inc +filter_obj=*.o +filter_lib=*.lib +filter_lkr=*.lkr +[CAT_SUBFOLDERS] +subfolder_src= +subfolder_inc= +subfolder_obj= +subfolder_lib= +subfolder_lkr= +[FILE_SUBFOLDERS] +file_000=. +file_001=. +file_002=. +file_003=. +file_004=. +file_005=. +file_006=. +file_007=. +[GENERATED_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +[OTHER_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +[FILE_INFO] +file_000=firmware1704.asm +file_001=C:\Program Files\Microchip\MPASM Suite\p16f1704.inc +file_002=crc7.inc +file_003=crc16hi.inc +file_004=crc16lo.inc +file_005=flip.inc +file_006=icon.inc +file_007=inv.inc +[SUITE_INFO] +suite_guid={6B3DAA78-59C1-46DD-B6AA-DBDAE4E06484} +suite_state=generate-absolute-code +[TOOL_SETTINGS] +TS{DD2213A8-6310-47B1-8376-9430CDFC013F}= +TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/o"$(BINDIR_)$(TARGETBASE).cof" /M"$(BINDIR_)$(TARGETBASE).map" /W +TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}= +[INSTRUMENTED_TRACE] +enable=0 +transport=0 +format=0 +[CUSTOM_BUILD] +Pre-Build= +Pre-BuildEnabled=1 +Post-Build= +Post-BuildEnabled=1 diff --git a/firmware1704/flip.inc b/firmware1704/flip.inc new file mode 100644 index 0000000..cb9e8a4 --- /dev/null +++ b/firmware1704/flip.inc @@ -0,0 +1,257 @@ +;LUT for flipping bytes left to right for use with USART as IWM transmitter + retlw B'00000000' + retlw B'10000000' + retlw B'01000000' + retlw B'11000000' + retlw B'00100000' + retlw B'10100000' + retlw B'01100000' + retlw B'11100000' + retlw B'00010000' + retlw B'10010000' + retlw B'01010000' + retlw B'11010000' + retlw B'00110000' + retlw B'10110000' + retlw B'01110000' + retlw B'11110000' + retlw B'00001000' + retlw B'10001000' + retlw B'01001000' + retlw B'11001000' + retlw B'00101000' + retlw B'10101000' + retlw B'01101000' + retlw B'11101000' + retlw B'00011000' + retlw B'10011000' + retlw B'01011000' + retlw B'11011000' + retlw B'00111000' + retlw B'10111000' + retlw B'01111000' + retlw B'11111000' + retlw B'00000100' + retlw B'10000100' + retlw B'01000100' + retlw B'11000100' + retlw B'00100100' + retlw B'10100100' + retlw B'01100100' + retlw B'11100100' + retlw B'00010100' + retlw B'10010100' + retlw B'01010100' + retlw B'11010100' + retlw B'00110100' + retlw B'10110100' + retlw B'01110100' + retlw B'11110100' + retlw B'00001100' + retlw B'10001100' + retlw B'01001100' + retlw B'11001100' + retlw B'00101100' + retlw B'10101100' + retlw B'01101100' + retlw B'11101100' + retlw B'00011100' + retlw B'10011100' + retlw B'01011100' + retlw B'11011100' + retlw B'00111100' + retlw B'10111100' + retlw B'01111100' + retlw B'11111100' + retlw B'00000010' + retlw B'10000010' + retlw B'01000010' + retlw B'11000010' + retlw B'00100010' + retlw B'10100010' + retlw B'01100010' + retlw B'11100010' + retlw B'00010010' + retlw B'10010010' + retlw B'01010010' + retlw B'11010010' + retlw B'00110010' + retlw B'10110010' + retlw B'01110010' + retlw B'11110010' + retlw B'00001010' + retlw B'10001010' + retlw B'01001010' + retlw B'11001010' + retlw B'00101010' + retlw B'10101010' + retlw B'01101010' + retlw B'11101010' + retlw B'00011010' + retlw B'10011010' + retlw B'01011010' + retlw B'11011010' + retlw B'00111010' + retlw B'10111010' + retlw B'01111010' + retlw B'11111010' + retlw B'00000110' + retlw B'10000110' + retlw B'01000110' + retlw B'11000110' + retlw B'00100110' + retlw B'10100110' + retlw B'01100110' + retlw B'11100110' + retlw B'00010110' + retlw B'10010110' + retlw B'01010110' + retlw B'11010110' + retlw B'00110110' + retlw B'10110110' + retlw B'01110110' + retlw B'11110110' + retlw B'00001110' + retlw B'10001110' + retlw B'01001110' + retlw B'11001110' + retlw B'00101110' + retlw B'10101110' + retlw B'01101110' + retlw B'11101110' + retlw B'00011110' + retlw B'10011110' + retlw B'01011110' + retlw B'11011110' + retlw B'00111110' + retlw B'10111110' + retlw B'01111110' + retlw B'11111110' + retlw B'00000001' + retlw B'10000001' + retlw B'01000001' + retlw B'11000001' + retlw B'00100001' + retlw B'10100001' + retlw B'01100001' + retlw B'11100001' + retlw B'00010001' + retlw B'10010001' + retlw B'01010001' + retlw B'11010001' + retlw B'00110001' + retlw B'10110001' + retlw B'01110001' + retlw B'11110001' + retlw B'00001001' + retlw B'10001001' + retlw B'01001001' + retlw B'11001001' + retlw B'00101001' + retlw B'10101001' + retlw B'01101001' + retlw B'11101001' + retlw B'00011001' + retlw B'10011001' + retlw B'01011001' + retlw B'11011001' + retlw B'00111001' + retlw B'10111001' + retlw B'01111001' + retlw B'11111001' + retlw B'00000101' + retlw B'10000101' + retlw B'01000101' + retlw B'11000101' + retlw B'00100101' + retlw B'10100101' + retlw B'01100101' + retlw B'11100101' + retlw B'00010101' + retlw B'10010101' + retlw B'01010101' + retlw B'11010101' + retlw B'00110101' + retlw B'10110101' + retlw B'01110101' + retlw B'11110101' + retlw B'00001101' + retlw B'10001101' + retlw B'01001101' + retlw B'11001101' + retlw B'00101101' + retlw B'10101101' + retlw B'01101101' + retlw B'11101101' + retlw B'00011101' + retlw B'10011101' + retlw B'01011101' + retlw B'11011101' + retlw B'00111101' + retlw B'10111101' + retlw B'01111101' + retlw B'11111101' + retlw B'00000011' + retlw B'10000011' + retlw B'01000011' + retlw B'11000011' + retlw B'00100011' + retlw B'10100011' + retlw B'01100011' + retlw B'11100011' + retlw B'00010011' + retlw B'10010011' + retlw B'01010011' + retlw B'11010011' + retlw B'00110011' + retlw B'10110011' + retlw B'01110011' + retlw B'11110011' + retlw B'00001011' + retlw B'10001011' + retlw B'01001011' + retlw B'11001011' + retlw B'00101011' + retlw B'10101011' + retlw B'01101011' + retlw B'11101011' + retlw B'00011011' + retlw B'10011011' + retlw B'01011011' + retlw B'11011011' + retlw B'00111011' + retlw B'10111011' + retlw B'01111011' + retlw B'11111011' + retlw B'00000111' + retlw B'10000111' + retlw B'01000111' + retlw B'11000111' + retlw B'00100111' + retlw B'10100111' + retlw B'01100111' + retlw B'11100111' + retlw B'00010111' + retlw B'10010111' + retlw B'01010111' + retlw B'11010111' + retlw B'00110111' + retlw B'10110111' + retlw B'01110111' + retlw B'11110111' + retlw B'00001111' + retlw B'10001111' + retlw B'01001111' + retlw B'11001111' + retlw B'00101111' + retlw B'10101111' + retlw B'01101111' + retlw B'11101111' + retlw B'00011111' + retlw B'10011111' + retlw B'01011111' + retlw B'11011111' + retlw B'00111111' + retlw B'10111111' + retlw B'01111111' + retlw B'11111111' diff --git a/firmware1704/icon.inc b/firmware1704/icon.inc new file mode 100644 index 0000000..77abf38 --- /dev/null +++ b/firmware1704/icon.inc @@ -0,0 +1,289 @@ +;The TashTwenty logo in the form of a 32x32 bitmap icon with mask +;................................................................ +;................................................................ +;................................................................ +;................................................................ +;................................................................ +;................................................................ +;................................................................ +;................................................................ +;............................##################..######.......... +;........................###### ######## ####........ +;......................#### ######........ +;....................#### #### ## ########........ +;....................## ###### #### #### ##########.......... +;....................######## ## #### ########............ +;......................###### #### #### ####................ +;........................## #### #### ####................ +;........................## #### #### ####................ +;........................## ## #### ####................ +;........................## ## ###### ##########.......... +;........................#### ######## ###### ####........ +;..########################## ###### ## ############.. +;## #### ######## ## +;## ######## ########## ## +;## ######################## ## +;## ################ ## +;## ## +;## #### ## +;## ## +;## ## +;..############################################################.. +;................................................................ +;................................................................ + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x03 + retlw 0xFE + retlw 0xE0 + retlw 0x00 + retlw 0x0E + retlw 0x07 + retlw 0xB0 + retlw 0x00 + retlw 0x18 + retlw 0x00 + retlw 0x70 + retlw 0x00 + retlw 0x33 + retlw 0x40 + retlw 0xF0 + retlw 0x00 + retlw 0x2E + retlw 0xDB + retlw 0xE0 + retlw 0x00 + retlw 0x3C + retlw 0x9B + retlw 0xC0 + retlw 0x00 + retlw 0x1D + retlw 0x9B + retlw 0x00 + retlw 0x00 + retlw 0x09 + retlw 0x9B + retlw 0x00 + retlw 0x00 + retlw 0x09 + retlw 0x9B + retlw 0x00 + retlw 0x00 + retlw 0x08 + retlw 0x9B + retlw 0x00 + retlw 0x00 + retlw 0x08 + retlw 0xBB + retlw 0xE0 + retlw 0x00 + retlw 0x0C + retlw 0x7B + retlw 0xB0 + retlw 0x7F + retlw 0xFC + retlw 0x3A + retlw 0x7E + retlw 0x80 + retlw 0x06 + retlw 0x00 + retlw 0xF1 + retlw 0x80 + retlw 0x07 + retlw 0x83 + retlw 0xE1 + retlw 0x80 + retlw 0x03 + retlw 0xFF + retlw 0xC1 + retlw 0x80 + retlw 0x00 + retlw 0xFF + retlw 0x01 + retlw 0x80 + retlw 0x00 + retlw 0x00 + retlw 0x01 + retlw 0x8C + retlw 0x00 + retlw 0x00 + retlw 0x01 + retlw 0x80 + retlw 0x00 + retlw 0x00 + retlw 0x01 + retlw 0x80 + retlw 0x00 + retlw 0x00 + retlw 0x01 + retlw 0x7F + retlw 0xFF + retlw 0xFF + retlw 0xFE + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x03 + retlw 0xFE + retlw 0xE0 + retlw 0x00 + retlw 0x0F + retlw 0xFF + retlw 0xF0 + retlw 0x00 + retlw 0x1F + retlw 0xFF + retlw 0xF0 + retlw 0x00 + retlw 0x3F + retlw 0xFF + retlw 0xF0 + retlw 0x00 + retlw 0x3F + retlw 0xFF + retlw 0xE0 + retlw 0x00 + retlw 0x3F + retlw 0xFF + retlw 0xC0 + retlw 0x00 + retlw 0x1F + retlw 0xFF + retlw 0x00 + retlw 0x00 + retlw 0x0F + retlw 0xFF + retlw 0x00 + retlw 0x00 + retlw 0x0F + retlw 0xFF + retlw 0x00 + retlw 0x00 + retlw 0x0F + retlw 0xFF + retlw 0x00 + retlw 0x00 + retlw 0x0F + retlw 0xFF + retlw 0xE0 + retlw 0x00 + retlw 0x0F + retlw 0xFF + retlw 0xF0 + retlw 0x7F + retlw 0xFF + retlw 0xFF + retlw 0xFE + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0xFF + retlw 0x7F + retlw 0xFF + retlw 0xFF + retlw 0xFE + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 + retlw 0x00 diff --git a/firmware1704/inv.inc b/firmware1704/inv.inc new file mode 100644 index 0000000..96acbfc --- /dev/null +++ b/firmware1704/inv.inc @@ -0,0 +1,257 @@ +;LUT for translating IWM inversions into IWM bytes + retlw 0x80 + retlw 0x81 + retlw 0x83 + retlw 0x82 + retlw 0x86 + retlw 0x87 + retlw 0x85 + retlw 0x84 + retlw 0x8C + retlw 0x8D + retlw 0x8F + retlw 0x8E + retlw 0x8A + retlw 0x8B + retlw 0x89 + retlw 0x88 + retlw 0x98 + retlw 0x99 + retlw 0x9B + retlw 0x9A + retlw 0x9E + retlw 0x9F + retlw 0x9D + retlw 0x9C + retlw 0x94 + retlw 0x95 + retlw 0x97 + retlw 0x96 + retlw 0x92 + retlw 0x93 + retlw 0x91 + retlw 0x90 + retlw 0xB0 + retlw 0xB1 + retlw 0xB3 + retlw 0xB2 + retlw 0xB6 + retlw 0xB7 + retlw 0xB5 + retlw 0xB4 + retlw 0xBC + retlw 0xBD + retlw 0xBF + retlw 0xBE + retlw 0xBA + retlw 0xBB + retlw 0xB9 + retlw 0xB8 + retlw 0xA8 + retlw 0xA9 + retlw 0xAB + retlw 0xAA + retlw 0xAE + retlw 0xAF + retlw 0xAD + retlw 0xAC + retlw 0xA4 + retlw 0xA5 + retlw 0xA7 + retlw 0xA6 + retlw 0xA2 + retlw 0xA3 + retlw 0xA1 + retlw 0xA0 + retlw 0xE0 + retlw 0xE1 + retlw 0xE3 + retlw 0xE2 + retlw 0xE6 + retlw 0xE7 + retlw 0xE5 + retlw 0xE4 + retlw 0xEC + retlw 0xED + retlw 0xEF + retlw 0xEE + retlw 0xEA + retlw 0xEB + retlw 0xE9 + retlw 0xE8 + retlw 0xF8 + retlw 0xF9 + retlw 0xFB + retlw 0xFA + retlw 0xFE + retlw 0xFF + retlw 0xFD + retlw 0xFC + retlw 0xF4 + retlw 0xF5 + retlw 0xF7 + retlw 0xF6 + retlw 0xF2 + retlw 0xF3 + retlw 0xF1 + retlw 0xF0 + retlw 0xD0 + retlw 0xD1 + retlw 0xD3 + retlw 0xD2 + retlw 0xD6 + retlw 0xD7 + retlw 0xD5 + retlw 0xD4 + retlw 0xDC + retlw 0xDD + retlw 0xDF + retlw 0xDE + retlw 0xDA + retlw 0xDB + retlw 0xD9 + retlw 0xD8 + retlw 0xC8 + retlw 0xC9 + retlw 0xCB + retlw 0xCA + retlw 0xCE + retlw 0xCF + retlw 0xCD + retlw 0xCC + retlw 0xC4 + retlw 0xC5 + retlw 0xC7 + retlw 0xC6 + retlw 0xC2 + retlw 0xC3 + retlw 0xC1 + retlw 0xC0 + retlw 0xC0 + retlw 0xC1 + retlw 0xC3 + retlw 0xC2 + retlw 0xC6 + retlw 0xC7 + retlw 0xC5 + retlw 0xC4 + retlw 0xCC + retlw 0xCD + retlw 0xCF + retlw 0xCE + retlw 0xCA + retlw 0xCB + retlw 0xC9 + retlw 0xC8 + retlw 0xD8 + retlw 0xD9 + retlw 0xDB + retlw 0xDA + retlw 0xDE + retlw 0xDF + retlw 0xDD + retlw 0xDC + retlw 0xD4 + retlw 0xD5 + retlw 0xD7 + retlw 0xD6 + retlw 0xD2 + retlw 0xD3 + retlw 0xD1 + retlw 0xD0 + retlw 0xF0 + retlw 0xF1 + retlw 0xF3 + retlw 0xF2 + retlw 0xF6 + retlw 0xF7 + retlw 0xF5 + retlw 0xF4 + retlw 0xFC + retlw 0xFD + retlw 0xFF + retlw 0xFE + retlw 0xFA + retlw 0xFB + retlw 0xF9 + retlw 0xF8 + retlw 0xE8 + retlw 0xE9 + retlw 0xEB + retlw 0xEA + retlw 0xEE + retlw 0xEF + retlw 0xED + retlw 0xEC + retlw 0xE4 + retlw 0xE5 + retlw 0xE7 + retlw 0xE6 + retlw 0xE2 + retlw 0xE3 + retlw 0xE1 + retlw 0xE0 + retlw 0xA0 + retlw 0xA1 + retlw 0xA3 + retlw 0xA2 + retlw 0xA6 + retlw 0xA7 + retlw 0xA5 + retlw 0xA4 + retlw 0xAC + retlw 0xAD + retlw 0xAF + retlw 0xAE + retlw 0xAA + retlw 0xAB + retlw 0xA9 + retlw 0xA8 + retlw 0xB8 + retlw 0xB9 + retlw 0xBB + retlw 0xBA + retlw 0xBE + retlw 0xBF + retlw 0xBD + retlw 0xBC + retlw 0xB4 + retlw 0xB5 + retlw 0xB7 + retlw 0xB6 + retlw 0xB2 + retlw 0xB3 + retlw 0xB1 + retlw 0xB0 + retlw 0x90 + retlw 0x91 + retlw 0x93 + retlw 0x92 + retlw 0x96 + retlw 0x97 + retlw 0x95 + retlw 0x94 + retlw 0x9C + retlw 0x9D + retlw 0x9F + retlw 0x9E + retlw 0x9A + retlw 0x9B + retlw 0x99 + retlw 0x98 + retlw 0x88 + retlw 0x89 + retlw 0x8B + retlw 0x8A + retlw 0x8E + retlw 0x8F + retlw 0x8D + retlw 0x8C + retlw 0x84 + retlw 0x85 + retlw 0x87 + retlw 0x86 + retlw 0x82 + retlw 0x83 + retlw 0x81 + retlw 0x80