In USBSystem.bas there are two helper functions to set/get ram memory indirectly by address using the FSR registers:
Code: Select all
inline sub SetRAMPtr(pAddress as FSR0, pValue as POSTINC0)
end sub
inline function GetRAMPtr(pAddress as FSR1) as POSTINC1
end function
However, in the sub USBCtrlTrfRxService() I used them together in one statement:
Code: Select all
SetRAMPtr(pDst.bRam, GetRAMPtr(pSrc.bRam))
Code: Select all
?I000291_F010_001657_P000032 ; L#MK SETRAMPTR(PDST.BRAM, GETRAMPTR(PSRC.BRAM))
MOVFF M7_U16H,_FSR1L#F0_U16H
MOVFF M7_U16,_FSR1L#F0_U16
MOVFF POSTINC1,POSTINC0 ;<<< COPY IS DONE BEFORE FSR0 IS SET!!
MOVFF M9_U16H,_FSR0L#F0_U16H
MOVFF M9_U16,_FSR0L#F0_U16
to wherever FSR0 happens to be pointing to initially, trashing some (random) memory contents!
It turns out that I was too smart for my own good. If you look closely at the routine
Code: Select all
inline sub SetRAMPtr(pAddress as FSR0, pValue as POSTINC0)
end sub
That works as long as the parameters are evaluated left to right. But, when I used a function call as 'pValue', I forced the
compiler to evaluate that first!. So, 'SetRAMPtr()' as coded is seriously broken since it relies on a certain order of evaluation.
By replacing the sub with a macro, this can be fixed. A better implementation would be:
Code: Select all
macro SetRAMPtr(pAddress, pValue)
FSR0 = pAddress
POSTINC0 = pValue
end macro
Code: Select all
sub USBCtrlTrfRxService()
// This code executes in the Data stage of a Control Write transfer
dim byte_to_read as word // count of bytes to copy
// the two msb's of the count are in two lsb's of BDxSTAT (BC9, BC8)
byte_to_read.byte1 = ep0Bo.Stat._byte and $03
byte_to_read.byte0 = ep0Bo.Cnt
// Accumulate total number of bytes read
wCount = wCount + byte_to_read
// copy bytes from src (FSR1) to dest (FSR0)
pSrc.bRam = addressof(CtrlTrfData)
FSR1 = pSrc.bRam
FSR0 = pDst.bRam
while (byte_to_read > 0)
POSTINC0 = POSTINC1
dec(byte_to_read)
end while
// and update buffer pointers
pSrc.bRam = FSR1
pDst.bRam = FSR0
end sub
Optional Changes:
The compiler changed somewhat back around V2.2.1.4 or so, and if you compile the existing code for interrupt
mode (#option USB_SERVICE = true), you'll see a number of warning messages such as
If you wish to get rid of these warnings, locate the following misc routines in USBSystem.bas:[Warning] usbsystem.bas: Inline procedure called from within context save block, disabling inline code generation
Code: Select all
inline sub Nop()
asm
Nop
end asm
end sub
inline function GetRAMPtr(pAddress as FSR1) as POSTINC1
end function
inline function ReadROM() as TABLAT
asm
TBLRD *+
end asm
end function
inline function GetROMPtr(pAddress as TABLEPTR) as TABLAT
ReadROM()
end function
Code: Select all
macro nop()
asm
Nop
end asm
end macro
function GetRAMPtr(pAddress as FSR1) as POSTINC1
end function
function ReadROM() as TABLAT
asm
TBLRD *+
end asm
end function
function GetROMPtr(pAddress as TABLEPTR) as TABLAT
asm
TBLRD *+
end asm
end function
While you're making changes there's another goof... I seem to have left some debug code in the CDC interrupt handler
file USBCDC.bas:
Code: Select all
#if (USB_SERVICE)
interrupt USB_intr_handler(PRIORITY_LEVEL)
high(porta.0) << ****REMOVE THIS LINE****
// check for USB intr request
if ((PIR2Bits.USBIF = 1) and (PIE2Bits.USBIE = 1)) then
PIR2Bits.USBIF = 0
save(0, FSR0, FSR1, _service)
_service()
restore
endif
low(porta.0) << ****REMOVE THIS LINE****
end interrupt
#endif