summaryrefslogtreecommitdiff
path: root/usr.bin/pcc/nova/README
blob: d50699be54ddde9b865b714d5a4c6020dc7edebd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
$OpenBSD: README,v 1.1 2007/10/07 17:58:51 otto Exp $

Calling conventions, stack frame and zero page:

The variables that normally are placed on the stack or in registers in C
are instead allocated in the zero page and saved on a (fictive) stack
when calling functions.  Some locations have predefined functions though.
Arrays allocated as automatics are stored on the stack with a pointer
in zero page to its destination.

0-7	Unused
10	Stack pointer
11	Frame pointer
12-14	Unused
15	Used by prolog
16	Prolog address, written in crt0
17	Epilog address, written in crt0
20-27	Auto-increment, scratch
30-37	Auto-decrement, scratch
40-47	Unused
50-57	Scratch/Arguments
60-77	Permanent, save before use.
100-377	Addresses for subroutines, written by the assembler

The normal registers (AC0-AC3) are all considered scratch registers.

Register classes are assigned as:
	AC0-AC3: AREGs.
	AC2-AC3: BREGs.
	50-77:	 CREGs.
	...and eventually register pairs as DREGs.

In byte code the low half of a word is the first byte (little-endian).
This is bit 8-15 in Nova syntax.

The stack is growing towards lower adresses (as opposed to the Eclipse stack).
Stack layout:

	! arg1	!
	! arg0	!
 fp ->	! old pc!
	! old fp!
 pc ->	! saved !

A reference to a struct member in assembler, a = b->c; b is in ZP 50
+ is zeropage-addressing
* is fp-adressing

# offset 0
+	lda 0,@50	# load value from indirect ZP 50 into ac0
*	lda 2,,3	# load value from (ac3) into ac2
*	lda 0,,2	# load value from (ac2) into ac0

# offset 12
+	lda 2,50	# load value from ZP 50 into ac2
+	lda 0,12,2	# load value from (ac2+12) into ac0
*	lda 2,,3	# load value from (ac3) into ac2
*	lda 0,12,2	# load value from 12(ac2) into ac0

# offset 517
+	lda 2,50	# load value from ZP 50 into ac2
+	lda 0,.L42-.,1	# load offset from .L42 PC-indexed
+	addz 0,2,skp	# add offset to ac2 and skip
+.L42:	.word 517	# offset value
+	lda 0,,2	# load value from (ac2) into ac0

The prolog/epilog implementation; it is implemented as subroutines.

.L42:	.word 13	# number of words to save
func:
	sta 3,@40	# save return address on stack
	lda 2,.L42-.,1	# get save word count
	jsr @45		# go to prolog
	...
	lda 2,.L42-.,1	# get restore word count
	jmp @46		# jump to epilog

# words to save in 2, return address in 3
prolog:
	sta 2,45	# save # of words to move at scratch
	lda 0,41	# get old fp
	lda 1,40	# get sp
	sta 1,41	# save new fp
	dsz 40		# decrement stack, will never be 0
	sta 0,@40	# save old fp
	dsz 40

	lda 0,off57	# fetch address of regs to save - 1
	sta 0,20	# store address at autoincr
1:	lda 0,@20	# get word to copy
	sta 0,@40	# push on stack
	dsz 40		# manually decrement sp
	dsz 45		# copied all words?
	jmp 1b,1	# no, continue
	jmp 0,3		# return

epilog:
	sta 2,45	# save # of words to move at scratch

	lda 3,off57	# fetch address of regs to save
	sta 3,20	# store at autoincr
	lda 3,41	# fetch fp
	sta 3,30	# store at autodecr
	lda 3,@30	# get old fp

1:	lda 2,@30	# fetch word from stack
	sta 2,@20	# store at orig place
	dsz 45		# enough?
	jmp 1b,1	# no, continue

	lda 2,41	# get new fp
	sta 2,40	# restore stack
	sta 3,41	# restore old fp
	jmp @40		# Return

Assembler syntax and functions.

The assembler syntax mimics the DG assembler.
Load and store to addresses is written "lda 0,foo" to load from address foo.
If foo is not in zero page then the assembler will put the lda in the
text area close to the instruction and do an indirect pc-relative load.