Here's the example of an organized Assembler program that I promised. I'll try to explain how things are organized.
I do not use linker to assemble modules. Rather I use include files (let's call them units). This way, most things get resolved during compile time.
Before units get included, I define different variables, which then get used by units to shape the code. For example, in this program, the first four variables get defined to alter behaviour of the init unit. POWERSAVE tells it that PIC should go to slip/idle when it's nothing to do. Without it, it would do simply loop through NOP commands. CLOCKSOURCE tells that there's a watch crystal on SOSCI/SOSCO pins and it is to be used to generate regular interrupts. TUNE_8MHZ tells it to use built-in RC oscillator to clock the processor, and PLL=2 tells it to run it 2 times faster than the oscillator. Frequency will be 16MHz or 8 MIPS, but it is sure irrelevant for this particular program.
Next 4 definition are for the display unit and they tell where the LCD lines are connected.
Then, units get included.
p24HInit.inc plays the same role as libc for C, with much less capabilities, of course. It does a lot of stuff - sets the clock for the CPU, defines TCY to the duration of single instruction in ns, sets up timer interrupts to create periodically-called functions. It also defines some system macros, such as alloc_dma to fetch the next available DMA module number, alloc_flag to create a bit flag and lots of other small stuff. It maintains time in a varible called sec_count. It also defines the code sections used later.
NHD0420DZW.inc is an LCD unit. It works with one particular kind of LCD. If I get a different one, I will simply replace this with a different unit. It uses the definitions of LCD pin connections from above to create functions to control LCD. It also uses TCY definition from the init unit to set the speed at wich PIC is going to talk to the LCD.
strings.inc is not used in this program, but it is required for RTCC.inc. It has macros such as strcpy or memcpy.
RTCC.inc is a real-time clock, which uses sec_count from the init unit to display time. It needs two things to work properly - offset between sec_count and real time and time zone shift. These are not set here, so the time displayed will be bogus.
Then we see .val section, which is allocated in program memory to represent constants, such as "Hello, World!" here.
The .nbss section hosts all data. Here it only contains a buffer for string manipulations.
The .text section contains code. __Init is called from within the init unit once all the PIC initialization is done. Here it initializes display and rtc units and then clears the "no sleep" flag which tells that it is ok to completely shut down CPU clock when there's nothing to do. Then it puts the "Hello, World!" on LCD.
__Loop is also called from the init unit. It is a "lazy loop" which is called approximately once a second. Since nothing going on in this particular program, it will be called exactly every second, but if CPU was busy this could be delayed. I could also use fast lazy loop, which would be called every time CPU wants to go idle.
The code in __Loop loads local time into the rtc buffer, then parses it into days, hours etc., then converts these things into a text string and puts it on the third line of the LCD. Sine __Loop is called every second, you get a running clock on the third line of LCD while the first line will still hold the "Hello, World!" phrase.
Note that all the functions from the display unit are prefixed with display_, and all the functions from the rtc unit are preficed with rtc_. This is an important aspect of program organization.
Of course, Assembler programs can be organized in a number of ways. This is just one of them.
Code:
.include "p24HJ64GP502.inc"
.equ POWERSAVE, 1
.equ CLOCKSOURCE, 1
.equ PLL, 2
.equ TUNE_8MHZ, 1
.equ DISPLAY_PORT, PORTB
.equ DISPLAY_SCL, 11
.equ DISPLAY_CS, 13
.equ DISPLAY_SDO, 12
.include "p24HInit.inc"
.include "NHD0420DZW.inc"
.include "strings.inc"
.include "RTCC.inc"
;----------------------------------------
.section .val
hello:
.asciz " Hello, World!"
.section .nbss
buffer:
.space 16
;----------------------------------------
.text
__init:
rcall display_init
rcall rtc_init
flag clr, #FLAG_NOSLEEP
mov #psvoffset(hello), w0
call display_write_text
return
;----------------------------------------
__loop:
rcall rtc_load_official
rcall rtc_parse
mov #3, w0
clr w1
rcall display_go_to
mov #buffer, w0
rcall rtc_datestring
mov #buffer, w0
rcall display_write_text
mov #buffer, w0
mov ' ',w1
mov.b w1,[w0++]
rcall rtc_timestring
mov #buffer, w0
rcall display_write_text
return
.end