Include

AVR Includes

Unfortunately the AVR IDE do not support libraries and the macro capabilities of the assembler in AVR Studio are very limited. So instead of building a real library I just wrote a collection of files I could include in the projects as I needed then. You need this include directory for most of my AVR projects.

Here you can download the Include Library

Interactive Monitor

My includes started with the monitor. During the development of AVR projects I often thought, that it would be cool to have some sort of monitor program I could call and have an interactive console to look into the processor or to have a command line so I could call some subā€routines to test things and gain experience with the AVR processors.

So I have written a small monitor program that is pretty much a copy of the Apple II monitor program as it was included in the original Apple II, of course there are some differences. E.g. there is no G (Go) command.

You need to include three modules. The first include defines all the necessary labels for the character table. Like the Apple II monitor each character is equivalent to a command.

*1000<200.2ffm

This moves memory block from 0x0200 to 0x02ff to 0x1000. In other words it just moves RAM. Other commands are inspect <CR>, verify, deposit, add, subtract etc.

For convenience I have added my own standard commands P (pattern) and x (hexdump).

After this you can define further command characters. In case you want a control sequence to act as a command character you just need to and it with 0x1F.

Of course you cannot use the characters A-F as they are used as hex digits.

After your own command characters you must include the second module this then defines the length of the character table. Followed by the addresses of the action routines of your own commands. Make sure the amount and order are in sync with the command character table.

.include        "../include/monitor-chartbl-v1-3.asm"
        .db             0, '?'
        .db             0, 'O'		;               Toggle Motor On         
        .db             0, 'S'		;               Toggle Drive Select
        .db             0, 'H'		;               Toggle Head Select
        .db             0, 'T'		;               Seek
        .db             0, 'R'		;               Read Track
        .db             0, 'K'		;               Analyze Track and show info
        .db             0, 'W'		;               Generate Write Pattern
        .db             0, 'Q'		;               Read Track using CPLD
        .db             0, 'I'		;
        .db             0, 'I' & 0x1F	;               
        .db             0, 'U'		;               Write using CPLD
        .db             0, 'J'		;               Print IDs
        .db             0, 'N'		;               Make CRC Table
        .db             0, 'Y'		;               CRC calculate aaaa.bbbby
        .db             0, 'Z'		;               CRC calculate optimized
        
;
;       AVR Monitor, inlcude the action routine address table of the default
;       commands followed by the addresses of the application specific 
;       command routines. Both tables must be in sync of course...
;
.include        "../include/monitor-subtbl-v1-3.asm"
        .dw             helpscreen
        .dw             togglemot
        .dw             toggleds
        .dw             togglehs
        .dw             seek
        .dw             readtrack
        .dw             analyzetrack
        .dw             writepattern
        .dw             quickread
        .dw             indexx
        .dw             initmfm
        .dw             writecpld
        .dw             printids
        .dw             crcmaketable
        .dw             crcupdate
        .dw             crcupdate2


.include        "../include/monitor-v1-6.asm"

Interface to Main Program

In order to interface with the monitor you need to define a serial input and output routine. They are called serin and serout. The routines are expected to preserve all registers. It’s up to you to initialise any IO device that will source and accept the input and output character. Mostly this will be one of the UARTs of the AVR processors.

Name Description
serin Serial Input routine, the monitor expects a new characeter in register `char`
serout Serial Output routine, the monitor puts the characeter in register `char`

To enter the monitor just jump to the label mon. This will first issue a prompt and then ask for input terminated by a <CR>.

Name Description
mon The monitor defines this label. If your main program just wants to call the monitor just jmp to this label. The monitor will never exit unless you build your own exit coammnd and routine
fakemon In case you want to call the monitor from your own interactive interface you can do so by calling `fakemon`. This entry assumes that the Y register points to a buffer with the monitor commands terminated by a ``.

Normally all addresses are treated as memory address. That is the monitor just uses the `lds` and `sts` instructions to load and store the appropriate value. This also gives access to the registers and the IO addresses. See the respective documentation of the microcontroller you are using. In case you want ot access different things (e.g. flash memory). you can write your own load and store routines. Just use the following labels.
Label Description
loaduser Takes precedence over the monitor load routine.
storeuser Takes precedence over the monitor store routine.

Registers

You need to define two registers char and temp which support immediate constants, i.e. registers r16 to r25.

Register Description
char Holds the character for serial input or output
temp Temporary Register
zero This register must hold the constant 0x00

The following registers are destroyed and must be saved by the caller.

xl, xh, yl, yh, zl, zh, r0

Action Commands

When writing your own action routines you can access the parameters you have previously entered.

There are several RAM locations defined by the monitor which are filled with the parameters. For each parameter there are 3 bytes, the lower 8 bits, the upper 8 bits and the bank. The bank address is entered and used as in the Apple IIgs monitor. On a AVR with only 16-bit addresses this is normally not required, but in the case I wanted to test the RLV12 Emulators DMA I required at least 22-bit addresses.

a1l, a1h, a1b

a3l, a3h, a3b

a4l, a4h, a4b

malloc and free

For a larger project I needed to dynamically allocate and dispose memory blocks. So I wrote my version of malloc() and free().

print

In my programs I often create debug or log messages. For a long time they all were customized routines. But then I decided to write a generalized print() sub routine. It is not as sophisticated as sprinft() and has a very special encoding of dynamic values. Directly following the call to the print routine you add the text string with the format codes

	call	print
	.db		"Text", 0x81, 0x80, 0x0d, 0x0a, 0x00

Format codes are defined as byte values with the MSB set. The upper nibble defines the format and the lower nibble is the index into a 16-byte storage area

	.dseg
pprint:	.byte 16

The format codes are

Code Description
0x8n Convert the byte at offset to two hex digit
0x9n Copy the character at offset
0xAn Convert the 16-bit integer at the offset to octal ASCII with leading zeroes
0xBn Copy the 24-bit integer at the offset to octal ASCII with leading zeroes
0xCn Conver the 16-bit integer at the offset to decimal ASCII with leading blanks
0xDn Convert the 32-bit integer at the offset to decimal ASCII with leading blanks and thousands delimiter

The print routine directly calls serout to send the characters to the UART with the user interface

macros

There are macros I use in almost every AVR project.

tparse

Starting with the RLV12 emulator I was looking for a decent user command line interface. Inspired by $TPARSE of the PDP-11 MACRO library I have written my own table driven parser.

SD Card Routines

My attempt to write low-level access routines for SD-Cards. It says v2-0 but it still is work in progress.

Programming Conventions

Over the time I standardised how I’m doing certain things in AVR assembler projects, mostly for may own sake.

Register Definitions

Whenver possible I work with register definitions. Whenever possible I avoid to use the register names directly. With some exception of r0 and r1 and my standard 32-bit integer which is using r4, r5, r6 and r7.

.def    miss    = r8		; Missed Interval Counter
.def    ff      = r9		; Constant 0xFF
.def    one     = r10		; Constant 0x01
.def    zero    = r11		; Constant 0x00
.def    crcl    = r12		; Used to calculate CRC
.def    crch    = r13		;            "
.def    crc3    = r14
.def    crc4    = r15
.def    temp    = r16		; Temporay register with immediate always destroyed!
.def    char    = r17		; The character or byte
.def    count   = r18		; General purpose counter
;

Important Constants

0x00, 0x01 and 0xFF are very often used as constants throughout my programs. In order to avoid using a temporary register and also to speed things I almost always define registers that are preset to one of these values. As the lower 16 registers of an AVR CPU cannot be used with immediate values they often are not used that often so you always have some spare register that as well can hold a constant themselves. Even the C-compilers use this.

Register Description
zero This is a register definition and is set to 0x00 during program initialisation, often used to initialise data or to set the direction register of an 8-bit port especially when I need a bidirectional port without the external memory interface
one This is a register definition and is set to 0x01 during program initialisation, a value I use surprisingly often
ff This is a register definition and is set to 0xFF during program initialisation, often used to set the direction of an 8-bit port

General Purpose Registers

The following registers hold certain values that are typically required in all sections and routines.

Register Description
char Holds the character for serial input or output, but later I use it as the register that holds an input or output byte. For example when I need to transfer a block of data or when I'm reading or writing data to a external device
temp Temporary Register. You always need a temporary register that is never guaranteed to be preserved and can always be used as temporary storage.
count There are so many places you need a loop counter so I decided to have one register being dedicated to this type of requirement

Reserved Interrupt Registers

Instead of saving all registers and the processor status on the stack I often dedicate a few register to interrupt service routines. As the AVR per default has only one interrupt level there is no conflict with multiple interrupts. Typically I have a isreg and a itemp register used to save the status register SREGand the temporary register at interrupt level.

Usage in Projects

I collect all my AVR assembler projects in a AVR projects folder. Typically I create a folder for every AVR assembler project. The include files are all located in the include folder located in the AVR projects folder.

/Dokumente/AVR-Projekte/
|-- AVRIML
|-- CF-Card-8-Bit
|-- CONSOLE11V2
|-- DMATest
|-- MFMEmulator
|-- MFMReader
|-- MFV11
|-- MFV11B
|-- MFV11v1
|-- MXV11
|-- Qbridge11
|-- Qbridge11-v2
|-- Romulus1st
|-- Romulus2nd
|-- include
|-- malloc
`-- vgacontrollerIV