Assembler for Padauk microcontroller

What is this?

I just started to learn Padauk microcontrollers and I did not find a pure assembler. I decided to write one. This is not a competitor of SDCC or other more advanced developent tools and programming languages but sometimes less is more. I tried to keep it as simple as possible make it easy to learn. If you have some assembly programming background you will learn to use it a few minutes. The syntax is very simple.
Keep in mind this is the first assembler I wrote, very experimental, not fully tested...
At this moment PFS154 is the only supported microcontroller. Maybe I will add PFS173 later. I don't plan to create assembler for OTP (One-Time Programmable) microcontrollers.

How to use?

You can write the source code in any plain text editor. Save the file as "a.asm" at the same folder where pfs154.py located. The assembler is written in Python 3. You can download Python for free. Open the pfs154.py file and click Run > Run Module or just hit F5.
The assembler will create a parsed.temp file (you can delete it) and an a.hex file. For Windows users if you don't want to download Python there is an PFS154.exe in downloads, you may run that in cmd. You can download the a.hex to PFS154 microcontroller with your free-pdk programmer.

 easypdkprog -n PFS154 write a.hex

Syntax

I tried to stick to the syntax you can find in PFS154 data sheet as much as possible. You should learn only a very few extra syntax. The assembler is case insensitive, lowercase and uppercase letters are threated equivalent.

Segments

There are 2 segments, .var for variables and .rom for code. .var is optional but if exists it should be before .rom

 .var
	; put variables here
	
 .rom
	; put code here

Declare variables

Padauk use M keyword for SRAM where we store our variables. But there is a very similar space called IO. This is for Special Function Registers (SFR). In my assembler we have to specify with M or IO prefix where we store our variable. First comes the varible name then the address with M or IO prefix, a 2 digit hex number for address and 'h' in the end represents its hex number. The good news you don't have to declare the SFR-s, I already declared them in program. You can find the list of already declared SFRs in pfs154.py source code after "var =" line.

 .var
    myvar  M2Ah
    clkmd  IO03h

Bit declaration

You can declare bits also. Flag bits are already declared like so

 .var
    mybit M14h.5
    Z IO00h.0	 ; Flag register Zero bit
    C io00h.1    ; Flag register Carry bit

If you have a declared varible like above the myvar, you can access any bit using the myvar.n syntax without declaring the bit. Port registers are already declared, PA, PB... If you want to access a bit for example PA.0 you can use it in the code - no need to declare it.

Code syntax

Labels must end with colon. (label:)
Immediate (literal, constant) values MUST start with #.
After .orgxxxh directive the code will start at the provided address. Only 3 digit hex number accepted here. Interrupt starts at 10h address so it is usefull here.
After call and goto you may use label or direct address. For calibration we have to call 0x7ed to return the calibration value. You can use the call $xxxh syntax, call $7edh in this case for IHRC calibration.

Code examples

Start-up code

 .rom
	wdreset

	mov  a,#9ch     ; setup clkmd, IHRC/64, disable watchdog
	mov  clkmd,a

	call $7edh      ; clock calibration
	mov  ihrcr,a

	call $7eeh      ; bandgap tuning
	mov  bgtr,a

	mov  a,#40h     ; set stack pointer 0x40 = 64
	mov  sp,a

I do recommend always set up the Clock Mode (clkmd), do the IHRC calibration, bandgap tuning and set up the stack pointer in the beginning of your code.

LED blinker

1sec on/off state will alter.

 .var
	t1  M00h
	t2  M01h
	t3  M02h
	
 .rom
	wdreset
	mov   a,#34h
	mov   clkmd,a
	call  $7edh
	mov   ihrcr,a
	call  $7eeh
	mov   bgtr,a
	mov   a,#40h	;0x40 = 64
	mov   sp,a
	set1  pac.0
	clear t1
	mov   a,#40
	mov   t2,a
	mov   a,#1
 lab1:
	xor   pa,a
	call  wait1s
	goto  lab1

 wait1s:
	pushaf
	clear t1
	clear t2
	mov   a,#24
	mov   t3,a
 w1:
	nop
	nop
	dzsn  t1
	goto  w1
	dzsn  t2
	goto  w1
	dzsn  t3
	goto  w1
	popaf
	ret

Advanced LED blinker :D

In this example you can see how to use T16 timer and interrupt. 264ms on/off state will alter.

 .var
	t1      M20h
	t2      M21h
	
 .rom
	wdreset
	goto    main
 .org010h                ; interrupt
	pushaf
	mov     a,#1
	xor     pa,a
	set0    intrq.2
	popaf
	reti

 main:
	mov     a,#34h
	mov     clkmd,a
	call    $7edh
	mov     ihrcr,a
	call    $7eeh
	mov     bgtr,a
	mov     a,#40h      ; 0x40 = 64
	mov     sp,a
	set1    pac.0
	
	clear   t1
	clear   t2
	stt16   t1
	
	set1    integs.4    ; falling edge
	mov     a, #9fh     ; IHRC, div=64, interrupt change on 15th bit
	mov     t16m, a
	
	set0    intrq.2     ; clear T16 irq flag
	set1    inten.2     ; enable T16 interrupt
	
	engint
	
 loop:	
	nop
	nop
	nop
	goto    loop

Downloads

PFS154 assembler
Note: source file must be named a.asm and it should be in the same folder where pfs154.py (or pfs154.exe) located.