;MACROS --------------------------------------------------------------------------
; DecBufPtr <ptr>
;  decrements buffer pointers and keeps them within one bank

; IncBufPtr <ptr>
;  increments buffer pointers and keeps them within one bank

; mmov <src>,<dest>,<Count>
;  Multi Move moves src to dest for count bytes.

; LookupW <12bitValue> [, <12bitValue>]
;  uses IREAD (affecting M and W) to lookup values up to 12 bits indexed by W

; Subroutine
;  Defines SubEntryAddr from the current address or the address of a jump from
;  space in the first low half page of memory as needed to ensure global 
;  CALL access to a subroutine.

; Push, Pop
;   compile code to push and pop W from a stack setup in one register bank.

mmov Macro 3
 local _bank
 noexpand
 _bank = 0
 rept \3
  IF ((\2 + %) / $10) != _bank
   _bank = (\2 + %) / $10
   expand
 bank (\2 + %)
   noexpand
   ENDIF
  expand
 mov w, \2 + %
  noexpand
  IF ((\1 + %) / $10) != _bank
   _bank = (\1 + %) / $10
   expand
 bank (\1 + %)
   noexpand
   ENDIF
  expand
 mov \1 + %, w
  noexpand
  ENDR
 ENDM


Push MACRO 1
	local parm
 noexpand
 parm = \1
 expand
 DecBufPtr StackPtr	;could use incsz rather than inc to avoid modifying Z
 noexpand
 IF Parm == Wreg || parm == fsr
  IF parm != fsr
   expand
 mov fsr, w	;fsr could be anything (due to bank etc..) so use for parm
   noexpand
  parm = WReg
   ENDIF
	expand
 mov w, StackPtr ;get the StackPtr into w
 xor fsr, w	;swap w with fsr
 xor w, fsr
 xor fsr, w
 mov ind, w	;store w to Top Of Stack.
	noexpand
 ELSE
	expand
 mov fsr, StackPtr ;W used
  noexpand
  IF parm > $0F
   expand
 bank parm
 mov w, parm
 bank Stack
 mov ind, w
   noexpand
  ELSE
   expand
 mov ind, parm
   noexpand
   ENDIF
  ENDIF
 ENDM
 
Pop MACRO 1
 noexpand
 expand
 mov fsr, StackPtr ;W used
 mov w, ind
 noexpand
 IF \1 > $0F
  expand
 bank \1
  noexpand
  ENDIF
  expand
 mov \1,w
 ;\1 is now the StackPtr
 IncBufPtr StackPtr ;point to valid data at new Top Of Stack
  noexpand
 ENDM

LookupW MACRO	;Version 2
 local _LookupWTableBegin, _LookupWTableEnd, _temp
 expand
;Defines an in-line DW/IREAD lookup table returns the 12 bit value indexed by W in M:W.
;Affects M and W.
 noexpand
 if CPUCarry == 1
  expand
 clc		;clc		to avoid adding an extra one
  noexpand
  endif
 expand
 page $+\0+3
 add w,2	;add w,2	make the index into an offset from the current location
 jmp $+\0+1
 noexpand
_LookupWTableBegin = $
 REPT \0
 expand
 DW \%
 noexpand
 ENDR
_LookupWTableEnd = $
 expand
 mov m,#_LookupWTableBegin>>8
 noexpand
 IF (_LookupWTableBegin / $100) != (_LookupWTableEnd / $100)
  expand
 snc		;correct if carry due to table spanning a half page
 mov m,#_LookupWTableBegin>>8+1	
  noexpand
 ENDIF
 expand
 iread 		;Retrieve data
 noexpand
 ;{use the data}
 ENDM

;==============================
;Ruben Jönsson
;AB Liros Electronic
;Box 9124, 200 39 Malmö, Sweden
;TEL INT +46 40142078
;FAX INT +46 40947388
;ruben@pp.sbbs.se
;==============================
;
;...a macro that will pack 3 
;characters at a time, from the string, into 2 program memory locations in 
;order to save program memory. 
;
;Since a program memory location can hold 12 bits it can store 1.5 
;characters if all 12 bits are used instead of using the RETLW instruction 
;which only can store 8 bits. Of course, I have to use the IREAD instruction 
;to read the characters. (The IREAD, by the way seems to have a problem when 
;used on the SX52 (and probably the 48), but It's quite easy to make a fix 
;for it.)
;
;I store the first character and the upper nibble of the third character in 
;the first memory location and the second character and the lower nibble of 
;the third character in the second memory location. The 3 characters 'ABC' 
;would be stored as
;
;dw $441,$342	;$41,$42,$43=ABC
;
;in 2 words of the 12 bit program memory.
;
;This is the (SASM) macro I use for this:

;--------------------------------------------------------------------------
;	Macro to pack 8 bit character strings into 12 bit program memory
;	words. 3 character per 2 words of program memory. 1st memory
;	location holds character 1 and upper nibble of character 3, 2nd
;	memory location holds charachter 2 and lower nibble of character
;	3, 3rd location holds character 4 and upper nibble of character
;	6 and so on...
;	If one string is split over several lines (several PackTXT macros)
;	make sure that it is split on an even length of 3 characters.

PackTXT	MACRO
  LOCAL char1, char2
  noexpand
  char1=-1   ;Temp variable for 1st character.
  char2=-1   ;Temp variable for 2nd character
  rept \0	    ;Repeat through all arguments to this
              ;macro (all characters).
    if ((%-1)//3 == 0)   ;Is it charachter 1 (4, 7...)?
      char1=\%	           ;Save it for later 
    endif
    if ((%-1)//3 == 1)   ;Is it character 2 (5, 8...)?
      char2=\%	           ;Save it for later.
    endif
    if ((%-1)//3 == 2)   ;If it is character 3 (6, 9...) we
                         ;have a group of 3 characters to
                         ;store 2 in memory words
      expand
      dw  (((\% & $f0) << 4) + char1),(((\% & $f) << 8) + char2)
      noexpand
      char1=-1
      char2=-1
      endif
  endr
  if char1 != -1         ;If the number of characters are 1 or 2
                         ;(4,5,7,8....) store character 1 (4,7...)
                         ;in a single word
    expand
    dw  char1
    noexpand
  endif
  if char2 != -1         ;If the number of characters are 2 (5,8...)
                         ;store character 2 (5,8...) in a single word.
    expand
    dw  char2
    noexpand
  endif	
endm

;With the Parallax assembler I can just write
;
;	PackTXT	'ABCDEFG'
;
;But the SASM assembler requires me to write
;
;	PackTXT	'A','B','C','D','E','F','G'

;Here is the code I have used for reading back my packed 
;strings. It could probably be more generalized regarding 
;register usage, but this is how it is done in my application.
;
;It uses two global registers, TMP1 and TMP3, and 2 non global 
;registers MEM_PTR_L and CHAR3. It takes an offset to the first 
;character in the string in a string table as input in W. This 
;function is optimized for a string table of up to 256 program 
;words - up to 384 characters (384 is 256 * 1.5 but this 
;requires all strings to be of a length even divisible by 3 - 
;which most likely isn't the case, making the maximum number of 
;characters somewhat less than 384). This routine also requires 
;all strings to be in the same 256 byte (half) page. I have used 
;a constant, LCD_END_STR, instead of 0 as an end marker for the 
;strings since I also need to send the character 0 (user defined 
;character and definition of this to an LCD).
;
;string_to_lcd_buff
;	bank		MEM_PTR_L
;	mov		MEM_PTR_L,w		;Offset in string table
;	clr		TMP3				;Byte read counter
;string_to_lcd_buff_loop
;	mov		m,#(rom_strings>>8);High nibble offset
;	mov		w,MEM_PTR_L		
;	iread					;Read character
;	inc		TMP3				;Inc byte read counter
;string_to_lcd_buff_001
;	mov		TMP1,w			;Save read byte.
;	xor		w,#LCD_END_STR		;Is it the end of the 
;string?
;	snz
;	retp						;Yes - exit
;	swap		CHAR3			;Make the 3rd character from 
;							;The two previously read
;	mov		w,#$f0			;Mask off the lower nibble
;	and		CHAR3,w
;	mov		w,m
;	and		w,#$f			;Add the high order nibble
;							;from this IREAD.
;	or		CHAR3,w
;	call		@putchar_lcd+1		;Sends TMP1 to LCD buff
;	sb		TMP3.1			;Is 2 bytes read?
;	jmp		not_third_char		;No - continue with next 
;char
;	clr		TMP3				;Yes - clear counter and
;	mov		w,CHAR3			;send the char in CHAR3
;	jmp		string_to_lcd_buff_001
;not_third_char
;	inc		MEM_PTR_L			;Increment pointer to next 
;							;memory location.
;	jmp		string_to_lcd_buff_loop
;



Subroutine MACRO
 noexpand
;Usage: Define a Global lable, 
; Execute Subroutine macro, 
; Assign :Entry to the value now set in SubEntryAddr. 
; Continue the definition of the subroutine. 
; Elsewhere, call @Sub:Entry where Sub is the global lable
;  you defined for the subroutine.
;Example
;SUB1	Subroutine 
;:Entry = SubEntryAddr
;....
;	Call SUB1:Entry
 _SubAddr = $
 IF (_SubAddr & $100) != 0 
  org LowHalfPage
  SubEntryAddr = $
;if we got here, the pagesel bits must be set for here
  IF ($ / $100) == (_SubAddr / $100)
   expand
 jmp _SubAddr
   noexpand
  ELSE
   expand
 jmp @_SubAddr
   noexpand
   ENDIF
  LowHalfPage = $
  IF $+1 > HighHalfPage
   ERROR 'Out of LowHalfPage Space'
   ENDIF
  org _SubAddr
 ELSE ;The subroutine was already starting in a LowHalfPage
  SubEntryAddr = $
  ENDIF
 ENDM


DecBufPtr MACRO 1
 noexpand
;decrements buffer pointers and keeps them within one bank
 IF CPUPins > 28
  expand
 dec \1
 setb \1.5
  noexpand
 ELSE
  expand
 dec \1
 setb \1.4
  noexpand
  ENDIF
 ENDM

IncBufPtr MACRO 1
 noexpand
;increments buffer pointers and keeps them within one bank
 IF CPUPins > 28
  expand
 inc \1
 setb \1.5
  noexpand
 ELSE
  expand
 inc \1
 setb \1.4
 clrb \1.5
  noexpand
  ENDIF
 ENDM

Inc24 MACRO 1
 inc \1+0
 snz
  inc \1+1
 snz
  inc \1+2
 ENDM

