mirror of
https://github.com/cc65/cc65.git
synced 2024-12-23 19:29:37 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
e2ad9efe41
13
.github/checks/Makefile
vendored
Normal file
13
.github/checks/Makefile
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
.PHONY: check tabs lastline spaces
|
||||
|
||||
check: tabs lastline spaces
|
||||
|
||||
tabs: tabs.sh
|
||||
@./tabs.sh
|
||||
|
||||
lastline: lastline.sh
|
||||
@./lastline.sh
|
||||
|
||||
spaces: spaces.sh
|
||||
@./spaces.sh
|
25
.github/checks/lastline.sh
vendored
Executable file
25
.github/checks/lastline.sh
vendored
Executable file
@ -0,0 +1,25 @@
|
||||
#! /bin/bash
|
||||
OLDCWD=`pwd`
|
||||
SCRIPT_PATH=`dirname $0`
|
||||
CHECK_PATH=.
|
||||
|
||||
cd $SCRIPT_PATH/../../
|
||||
|
||||
nl='
|
||||
'
|
||||
nl=$'\n'
|
||||
r1="${nl}$"
|
||||
FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | while read f; do
|
||||
t=$(tail -c2 $f; printf x)
|
||||
[[ ${t%x} =~ $r1 ]] || echo "$f"
|
||||
done`
|
||||
|
||||
cd $OLDCWD
|
||||
|
||||
if [ x"$FILES"x != xx ]; then
|
||||
echo "error: found following files that have no newline at the end:" >&2
|
||||
for n in $FILES; do
|
||||
echo $n >&2
|
||||
done
|
||||
exit -1
|
||||
fi
|
18
.github/checks/spaces.sh
vendored
Executable file
18
.github/checks/spaces.sh
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
#! /bin/bash
|
||||
OLDCWD=`pwd`
|
||||
SCRIPT_PATH=`dirname $0`
|
||||
CHECK_PATH=.
|
||||
|
||||
cd $SCRIPT_PATH/../../
|
||||
|
||||
FILES=`find $CHECK_PATH -type f \( -name \*.inc -o -name Makefile -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l ' $'`
|
||||
|
||||
cd $OLDCWD
|
||||
|
||||
if [ x"$FILES"x != xx ]; then
|
||||
echo "error: found dangling spaces in the following files:" >&2
|
||||
for n in $FILES; do
|
||||
echo $n >&2
|
||||
done
|
||||
exit -1
|
||||
fi
|
18
.github/checks/tabs.sh
vendored
Executable file
18
.github/checks/tabs.sh
vendored
Executable file
@ -0,0 +1,18 @@
|
||||
#! /bin/bash
|
||||
OLDCWD=`pwd`
|
||||
SCRIPT_PATH=`dirname $0`
|
||||
CHECK_PATH=.
|
||||
|
||||
cd $SCRIPT_PATH/../../
|
||||
|
||||
FILES=`find $CHECK_PATH -type f \( \( -name \*.inc -a \! -name Makefile.inc \) -o -name \*.cfg -o -name \*.\[chs\] -o -name \*.mac -o -name \*.asm -o -name \*.sgml \) -print | grep -v "libwrk/" | grep -v "testwrk/" | xargs grep -l $'\t'`
|
||||
|
||||
cd $OLDCWD
|
||||
|
||||
if [ x"$FILES"x != xx ]; then
|
||||
echo "error: found TABs in the following files:" >&2
|
||||
for n in $FILES; do
|
||||
echo $n >&2
|
||||
done
|
||||
exit -1
|
||||
fi
|
3
.github/workflows/build-on-pull-request.yml
vendored
3
.github/workflows/build-on-pull-request.yml
vendored
@ -21,6 +21,9 @@ jobs:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Do some simple style checks
|
||||
shell: bash
|
||||
run: make -j2 check
|
||||
- name: Build the tools.
|
||||
shell: bash
|
||||
run: make -j2 bin USER_CFLAGS=-Werror
|
||||
|
@ -46,6 +46,9 @@ jobs:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Do some simple style checks
|
||||
shell: bash
|
||||
run: make -j2 check
|
||||
- name: Build the tools.
|
||||
shell: bash
|
||||
run: |
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,3 +7,6 @@
|
||||
/testwrk/
|
||||
/wrk/
|
||||
/cc65.zip
|
||||
/util/atari/*.exe
|
||||
/util/gamate/*.exe
|
||||
|
||||
|
113
Contributing.md
Normal file
113
Contributing.md
Normal file
@ -0,0 +1,113 @@
|
||||
This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete.
|
||||
|
||||
(''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.)
|
||||
|
||||
# generally
|
||||
|
||||
* You must obey these rules when contributing new code or documentation to cc65. We are well aware that not all existing code may respect all rules outlined here - but this is no reason for you not to respect them.
|
||||
* One commit/patch/PR per issue. Do not mix several things unless they are very closely related.
|
||||
|
||||
# Codestyle rules
|
||||
|
||||
## All Sources
|
||||
|
||||
### TABs and spaces
|
||||
|
||||
This is an ongoing controversial topic - everyone knows that. However, the following is how we do it :)
|
||||
|
||||
* TAB characters must be expanded to spaces.
|
||||
* 4 spaces per indention level (rather than 8) are preferred, especially if there are many different levels.
|
||||
* No extra spaces at the end of lines.
|
||||
* All text files must end with new-line characters. Don't leave the last line "dangling".
|
||||
|
||||
The (bash) scipts used to check the above rules can be found in ```.github/check```. You can also run all checks using ```make check```.
|
||||
|
||||
### misc
|
||||
|
||||
* 80 characters is the desired maximum width of files. But, it isn't a "strong" rule; sometimes, you will want to type longer lines, in order to keep the parts of expressions or comments together on the same line.
|
||||
* You should avoid typing non-ASCII characters.
|
||||
* If you change "normal" source code into comments, then you must add a comment about why that code is a comment.
|
||||
* When you want to create a comment from several lines of code, you should use preprocessor lines, instead of ```/* */``` or "```;```". Example:
|
||||
<pre>
|
||||
#if 0
|
||||
one ();
|
||||
two ();
|
||||
three = two () + one ();
|
||||
#endif
|
||||
</pre>
|
||||
* You should type upper case characters for hex values.
|
||||
* When you type zero-page addresses in hexadecimal, you should type two hex characters (after the hex prefix). When you type non-zero-page addresses in hex, you should type four hex characters.
|
||||
* When you type lists of addresses, it is a good idea to sort them in ascending numerical order. That makes it easier for readers to build mental pictures of where things are in an address space. And, it is easier to see how big the variables and buffers are. Example:
|
||||
<pre>
|
||||
xCoord := $0703
|
||||
yCoord := $0705 ; (this address implies that xCoord is 16 bits)
|
||||
cmdbuf := $0706 ; (this address implies that yCoord is 8 bits)
|
||||
cmdlen := $0786 ; (this address implies that cmdbuf is 128 bytes)
|
||||
color := $0787
|
||||
</pre>
|
||||
|
||||
## C Sources
|
||||
|
||||
* Your files should obey the C89 standard.
|
||||
* All declarations in a block must be at the beginning of that block.
|
||||
* You should put a blank line between a list of local variable declarations and the first line of code.
|
||||
* You must use ANSI C comments (```/* */```); you must not use C++ comments (```//```).
|
||||
* The normal indentation width should be four spaces.
|
||||
* When a function's argument list wraps around to a next line, you should indent that next line by either the normal width or enough spaces to align it with the arguments on the previous line.
|
||||
* When you add functions to an existing file, you should separate them by the same number of blank lines that separate the functions that already are in that file.
|
||||
|
||||
(The next two rules will be changed at some time in the future; but, for now:)
|
||||
|
||||
* You must separate function names and parameter/argument lists by one space.
|
||||
* When declaring/defining pointers, you must put the asterisk (```*```) next to the data type, with a space between it and the variable's name. Examples:
|
||||
<pre>
|
||||
int* namedPtr[5];
|
||||
char* nextLine (FILE* f);
|
||||
</pre>
|
||||
|
||||
## Assembly Sources
|
||||
|
||||
* Op-code mnemonics must have lower-case letters. The names of instruction macroes may have upper-case letters.
|
||||
* Hexadecimal number constants should be used except where decimal or binary numbers make much more sense in that constant's context.
|
||||
* Hexadecimal letters should be upper-case.
|
||||
* When you set two registers or two memory locations to an immediate 16-bit zero, you should use the expressions ```#<$0000``` and ```#>$0000``` (they make it obvious where you are putting the lower and upper bytes).
|
||||
* If a function is declared to return a char-sized value, it actually must return an integer-sized value. (When cc65 promotes a returned value, it sometimes assumes that the value already is an integer.)
|
||||
* Functions, that are intended for a platform's system library, should be optimized as much as possible.
|
||||
* Sometimes, there must be a trade-off between size and speed. If you think that a library function won't be used often, then you should make it small. Otherwise, you should make it fast.
|
||||
* Comments that are put on the right side of instructions must be aligned (start in the same character columns).
|
||||
* Assembly source fields (label, operation, operand, comment) should start ''after'' character columns that are multiples of eight (such as 1, 9, 17, 33, and 41).
|
||||
|
||||
## LinuxDoc Sources
|
||||
|
||||
* TAB characters must be expanded to spaces.
|
||||
* All text files must end with new-line characters. Don't leave the last line "dangling".
|
||||
* 80 characters is the desired maximum width of files.
|
||||
* You should avoid typing non-ASCII characters.
|
||||
|
||||
* You should put blank lines between LinuxDoc sections:
|
||||
* Three blank lines between ```<sect>``` sections.
|
||||
* Two blank lines between ```<sect1>``` sections.
|
||||
* One blank line between other sections.
|
||||
|
||||
# Library implementation rules
|
||||
|
||||
* By default the toolchain must output a "standard" binary for the platform, no emulator formats, no extra headers used by tools. If the resulting binaries can not be run as is on emulators or eg flash cartridges, the process of converting them to something that can be used with these should be documented in the user manual.
|
||||
* Generally every function should live in a seperate source file - unless the functions are so closely related that splitting makes no sense.
|
||||
* Source files should not contain commented out code - if they do, there should be a comment that explains why that commented out code exists.
|
||||
|
||||
# Makefile rules
|
||||
|
||||
* Makefiles must generally work on both *nix (ba)sh and windows cmd.exe.
|
||||
* Makefiles must not use external tools that are not provided by the cc65 toolchain itself.
|
||||
|
||||
The only exception to the above are actions that are exclusive to the github actions - those may rely on bash and/or linux tools.
|
||||
|
||||
# Documentation rules
|
||||
|
||||
## User manual (LinuxDoc)
|
||||
|
||||
* This is the primary documentation.
|
||||
|
||||
## Wiki
|
||||
|
||||
* The Wiki is strictly for additional information that does not fit into the regular user manual (LinuxDoc). The wiki must not duplicate any information that is present in the user manual
|
5
Makefile
5
Makefile
@ -1,4 +1,4 @@
|
||||
.PHONY: all mostlyclean clean install zip avail unavail bin lib doc html info samples test util
|
||||
.PHONY: all mostlyclean clean install zip avail unavail bin lib doc html info samples test util check
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
@ -24,6 +24,9 @@ samples:
|
||||
test:
|
||||
@$(MAKE) -C test --no-print-directory $@
|
||||
|
||||
check:
|
||||
@$(MAKE) -C .github/checks --no-print-directory $@
|
||||
|
||||
util:
|
||||
@$(MAKE) -C util --no-print-directory $@
|
||||
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
[Documentation](https://cc65.github.io/doc)
|
||||
|
||||
[Wiki](https://github.com/cc65/wiki/wiki)
|
||||
[Contributing](Contributing.md) to the CC65 project.
|
||||
|
||||
The [Wiki](https://github.com/cc65/wiki/wiki) contains extra info that does not fit into the regular documentation.
|
||||
|
||||
[![Snapshot Build](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml/badge.svg?branch=master)](https://github.com/cc65/cc65/actions/workflows/snapshot-on-push-master.yml)
|
||||
|
||||
@ -27,6 +29,7 @@ including
|
||||
- the Atari 8-bit machines.
|
||||
- the Atari 2600 console.
|
||||
- the Atari 5200 console.
|
||||
- the Atari 7800 console.
|
||||
- GEOS for the C64, C128 and Apple //e.
|
||||
- the Bit Corporation Gamate console.
|
||||
- the NEC PC-Engine (aka TurboGrafx-16) console.
|
||||
|
8
asminc/atari7800.inc
Normal file
8
asminc/atari7800.inc
Normal file
@ -0,0 +1,8 @@
|
||||
; Atari 7800 TIA & RIOT read / write registers
|
||||
;
|
||||
; Karri Kaksonen (karri@sipo.fi), 2022
|
||||
|
||||
; TIA, RIOT & MARIA registers mapping
|
||||
.include "atari7800_tia.inc"
|
||||
.include "atari7800_riot.inc"
|
||||
.include "atari7800_maria.inc"
|
39
asminc/atari7800_maria.inc
Normal file
39
asminc/atari7800_maria.inc
Normal file
@ -0,0 +1,39 @@
|
||||
; Atari 7800 MARIA read / write registers
|
||||
;
|
||||
|
||||
; Read registers
|
||||
BKGRND := $20
|
||||
P0C1 := $21
|
||||
P0C2 := $22
|
||||
P0C3 := $23
|
||||
MWSYNC := $24
|
||||
P1C1 := $25
|
||||
P1C2 := $26
|
||||
P1C3 := $27
|
||||
MSTAT := $28
|
||||
P2C1 := $29
|
||||
P2C2 := $2A
|
||||
P2C3 := $2B
|
||||
DPPH := $2C
|
||||
P3C1 := $2D
|
||||
P3C2 := $2E
|
||||
P3C3 := $2F
|
||||
DPPL := $30
|
||||
P4C1 := $31
|
||||
P4C2 := $32
|
||||
P4C3 := $33
|
||||
CHBASE := $34
|
||||
P5C1 := $35
|
||||
P5C2 := $36
|
||||
P5C3 := $37
|
||||
OFFSET := $38
|
||||
P6C1 := $39
|
||||
P6C2 := $3A
|
||||
P6C3 := $3B
|
||||
CTRL := $3C
|
||||
P7C1 := $3D
|
||||
P7C2 := $3E
|
||||
P7C3 := $3F
|
||||
|
||||
; Write registers
|
||||
|
20
asminc/atari7800_riot.inc
Normal file
20
asminc/atari7800_riot.inc
Normal file
@ -0,0 +1,20 @@
|
||||
; Atari 7800 RIOT read / write registers
|
||||
;
|
||||
; Source: DASM - vcs.h
|
||||
; Details available in: Stella Programmer's Guide by Steve Wright
|
||||
;
|
||||
; Florent Flament (contact@florentflament.com), 2017
|
||||
|
||||
; Read registers
|
||||
SWCHA := $0280
|
||||
CTLSWA := $0281
|
||||
SWCHB := $0282
|
||||
CTLSWB := $0283
|
||||
INTIM := $0284
|
||||
TIMINT := $0285
|
||||
|
||||
; Write registers
|
||||
TIM1T := $0294
|
||||
TIM8T := $0295
|
||||
TIM64T := $0296
|
||||
T1024T := $0297
|
69
asminc/atari7800_tia.inc
Normal file
69
asminc/atari7800_tia.inc
Normal file
@ -0,0 +1,69 @@
|
||||
; Atari 7800 TIA read / write registers
|
||||
;
|
||||
; Source: DASM - vcs.h
|
||||
; Details available in: Stella Programmer's Guide by Steve Wright
|
||||
;
|
||||
; Florent Flament (contact@florentflament.com), 2017
|
||||
|
||||
; Read registers
|
||||
VSYNC := $00
|
||||
VBLANK := $01
|
||||
WSYNC := $02
|
||||
RSYNC := $03
|
||||
NUSIZ0 := $04
|
||||
NUSIZ1 := $05
|
||||
COLUP0 := $06
|
||||
COLUP1 := $07
|
||||
COLUPF := $08
|
||||
COLUBK := $09
|
||||
CTRLPF := $0A
|
||||
REFP0 := $0B
|
||||
REFP1 := $0C
|
||||
PF0 := $0D
|
||||
PF1 := $0E
|
||||
PF2 := $0F
|
||||
RESP0 := $10
|
||||
RESP1 := $11
|
||||
RESM0 := $12
|
||||
RESM1 := $13
|
||||
RESBL := $14
|
||||
AUDC0 := $15
|
||||
AUDC1 := $16
|
||||
AUDF0 := $17
|
||||
AUDF1 := $18
|
||||
AUDV0 := $19
|
||||
AUDV1 := $1A
|
||||
GRP0 := $1B
|
||||
GRP1 := $1C
|
||||
ENAM0 := $1D
|
||||
ENAM1 := $1E
|
||||
ENABL := $1F
|
||||
HMP0 := $20
|
||||
HMP1 := $21
|
||||
HMM0 := $22
|
||||
HMM1 := $23
|
||||
HMBL := $24
|
||||
VDELP0 := $25
|
||||
VDELP1 := $26
|
||||
VDELBL := $27
|
||||
RESMP0 := $28
|
||||
RESMP1 := $29
|
||||
HMOVE := $2A
|
||||
HMCLR := $2B
|
||||
CXCLR := $2C
|
||||
|
||||
; Write registers
|
||||
CXM0P := $00
|
||||
CXM1P := $01
|
||||
CXP0FB := $02
|
||||
CXP1FB := $03
|
||||
CXM0FB := $04
|
||||
CXM1FB := $05
|
||||
CXBLPF := $06
|
||||
CXPPMM := $07
|
||||
INPT0 := $08
|
||||
INPT1 := $09
|
||||
INPT2 := $0A
|
||||
INPT3 := $0B
|
||||
INPT4 := $0C
|
||||
INPT5 := $0D
|
@ -275,7 +275,7 @@ XFWRITE = $3B ; Only in TELEMON 3.x write file (bank 7 of Orix
|
||||
; Clock primitive
|
||||
XRECLK = $3C ; Reset clock
|
||||
XCLCL = $3D ; Close clock
|
||||
XWRCLK = $3E ; Displays clock in the adress in A & Y registers
|
||||
XWRCLK = $3E ; Displays clock in the address in A & Y registers
|
||||
|
||||
; Sound primitives
|
||||
XSONPS = $40 ; Send data to PSG register (14 values)
|
||||
|
67
cfg/atari7800.cfg
Normal file
67
cfg/atari7800.cfg
Normal file
@ -0,0 +1,67 @@
|
||||
# Atari VCS 7800 linker configuration file for cc65
|
||||
# In order to add the a78 header to the build you can add
|
||||
# "--force-import __EXEHDR__" to the command line
|
||||
|
||||
SYMBOLS {
|
||||
__STACKSIZE__: type = weak, value = $0600; # C stack
|
||||
__CARTSIZE__: type = weak, value = $8000;
|
||||
__VEC_BOTTOM__: value = $fffa, type = export;
|
||||
__VEC_SIZE__: value = $6, type = export;
|
||||
__ENCRYPT_BOTTOM__: value = $ff7a, type = export;
|
||||
__ENCRYPT_SIZE__: value = $80, type = export;
|
||||
__MEMORY_TOP__: value = __ENCRYPT_BOTTOM__, type = export;
|
||||
__INIT_SIZE__: value = 121, type = export;
|
||||
__MEMORY_INIT__: value = __MEMORY_TOP__ - __INIT_SIZE__, type = export;
|
||||
__MEMORY_BOTTOM__: value = $10000 - __CARTSIZE__, type = weak;
|
||||
__FREE_ROM_SIZE__: value = __MEMORY_INIT__ - __MEMORY_BOTTOM__, type = export;
|
||||
}
|
||||
|
||||
MEMORY {
|
||||
ZP: file = "", define = yes, start = $0040, size = $00C0, type = rw;
|
||||
SP: file = "", define = yes, start = $0140, size = $00C0, type = rw;
|
||||
RAM1: file = "", define = yes, start = $1800, size = $0840, type = rw;
|
||||
RAM2: file = "", define = yes, start = $2100, size = $0040, type = rw;
|
||||
RAM3: file = "", define = yes, start = $2200, size = $0600, type = rw;
|
||||
# For emulators you also need a header file
|
||||
HEADER: file = %O, start = $0000, size = 128;
|
||||
# "Normal" cartridge rom. Multiple banks arent supported
|
||||
# by this script. You may change the rom size, but keep
|
||||
# two things in mind:
|
||||
# - start must be a multiple of $1000
|
||||
# - ROM must end at $ff79
|
||||
ROM: file = %O, define = yes, start = __MEMORY_BOTTOM__, size = __FREE_ROM_SIZE__, type = ro, fill = yes, fillval = $ff;
|
||||
ROMS: file = %O, define = yes, start = __MEMORY_INIT__, size = __INIT_SIZE__, type = ro, fill = yes, fillval = $ff;
|
||||
# Encryption stuff
|
||||
ROME: file = %O, start = __ENCRYPT_BOTTOM__, size = __ENCRYPT_SIZE__, type = ro, fill = yes, fillval = $ff;
|
||||
# Interrupt vectors
|
||||
ROMV: file = %O, start = __VEC_BOTTOM__, size = __VEC_SIZE__, type = ro, fill = yes, fillval = $ff;
|
||||
}
|
||||
|
||||
SEGMENTS {
|
||||
ZEROPAGE: load = ZP, type = zp;
|
||||
EXEHDR: load = HEADER, type = ro, optional = yes;
|
||||
STARTUP: load = ROMS, type = ro, define = yes;
|
||||
ONCE: load = ROMS, type = ro, define = yes;
|
||||
CODE: load = ROM, type = ro, define = yes;
|
||||
RODATA: load = ROM, type = ro, define = yes, align = 256;
|
||||
DATA: load = ROM, run = RAM1, type = rw, define = yes;
|
||||
BSS: load = RAM1, type = bss, define = yes;
|
||||
VECTORS: load = ROMV, type = ro, define = yes;
|
||||
ENCRYPTION: load = ROME, type = ro define = yes;
|
||||
}
|
||||
|
||||
FEATURES {
|
||||
CONDES: type = constructor,
|
||||
label = __CONSTRUCTOR_TABLE__,
|
||||
count = __CONSTRUCTOR_COUNT__,
|
||||
segment = ONCE;
|
||||
CONDES: type = destructor,
|
||||
label = __DESTRUCTOR_TABLE__,
|
||||
count = __DESTRUCTOR_COUNT__,
|
||||
segment = RODATA;
|
||||
CONDES: type = interruptor,
|
||||
label = __INTERRUPTOR_TABLE__,
|
||||
count = __INTERRUPTOR_COUNT__,
|
||||
segment = RODATA,
|
||||
import = __CALLIRQ__;
|
||||
}
|
@ -29,7 +29,7 @@ MEMORY {
|
||||
EXT: file = "", define = yes, start = $9000, size = $1000;
|
||||
IO: file = "", define = yes, start = $A000, size = $1000;
|
||||
RAE1: file = "", define = yes, start = $B000, size = $1000;
|
||||
BASROM: file = "", define = yes, start = $C000, size = $1000;
|
||||
BASROM: file = "", define = yes, start = $C000, size = $2000;
|
||||
RAE2: file = "", define = yes, start = $E000, size = $1000;
|
||||
TOP: file = "", define = yes, start = $F000, size = $1000;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ MEMORY {
|
||||
EXT: file = "", define = yes, start = $9000, size = $1000;
|
||||
IO: file = "", define = yes, start = $A000, size = $1000;
|
||||
RAE1: file = "", define = yes, start = $B000, size = $1000;
|
||||
BASROM: file = "", define = yes, start = $C000, size = $1000;
|
||||
BASROM: file = "", define = yes, start = $C000, size = $2000;
|
||||
RAE2: file = "", define = yes, start = $E000, size = $1000;
|
||||
TOP: file = "", define = yes, start = $F000, size = $1000;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ MEMORY {
|
||||
EXT: file = "", define = yes, start = $9000, size = $1000;
|
||||
IO: file = "", define = yes, start = $A000, size = $1000;
|
||||
RAE1: file = "", define = yes, start = $B000, size = $1000;
|
||||
BASROM: file = "", define = yes, start = $C000, size = $1000;
|
||||
BASROM: file = "", define = yes, start = $C000, size = $2000;
|
||||
RAE2: file = "", define = yes, start = $E000, size = $1000;
|
||||
TOP: file = "", define = yes, start = $F000, size = $1000;
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ The names in the parentheses denote the symbols to be used for static linking of
|
||||
In memory constrained situations the memory from $803 to $1FFF
|
||||
can be made available to a program by calling <tt/_heapadd ((void *) 0x0803, 0x17FD);/
|
||||
at the beginning of <tt/main()/. Doing so is beneficial even if the program
|
||||
doesn't use the the heap explicitly because loading the driver (and in fact
|
||||
doesn't use the heap explicitly because loading the driver (and in fact
|
||||
already opening the driver file) uses the heap implicitly.
|
||||
|
||||
</descrip><p>
|
||||
|
@ -376,7 +376,7 @@ The names in the parentheses denote the symbols to be used for static linking of
|
||||
In memory constrained situations the memory from $803 to $1FFF
|
||||
can be made available to a program by calling <tt/_heapadd ((void *) 0x0803, 0x17FD);/
|
||||
at the beginning of <tt/main()/. Doing so is beneficial even if the program
|
||||
doesn't use the the heap explicitly because loading the driver (and in fact
|
||||
doesn't use the heap explicitly because loading the driver (and in fact
|
||||
already opening the driver file) uses the heap implicitly.
|
||||
|
||||
</descrip><p>
|
||||
|
157
doc/atari7800.sgml
Normal file
157
doc/atari7800.sgml
Normal file
@ -0,0 +1,157 @@
|
||||
<!doctype linuxdoc system>
|
||||
|
||||
<article>
|
||||
<title>Atari 7800 specific information for cc65
|
||||
<author>
|
||||
<url url="mailto:karri@sipo.fi" name="Karri Kaksonen"><newline>
|
||||
|
||||
<abstract>
|
||||
An overview over the Atari 7800 runtime system as it is implemented
|
||||
for the cc65 C compiler.
|
||||
</abstract>
|
||||
|
||||
<!-- Table of contents -->
|
||||
<toc>
|
||||
|
||||
<!-- Begin the document -->
|
||||
|
||||
<sect>Overview<p>
|
||||
|
||||
This file contains an overview of the Atari 7800 runtime system as it
|
||||
comes with the cc65 C compiler. It describes the memory layout, Atari
|
||||
7800 specific header files and any pitfalls specific to that platform.
|
||||
|
||||
<sect>Binary format<p>
|
||||
|
||||
The default binary output format generated by the linker for the Atari
|
||||
7800 target is a 48K cartridge image.
|
||||
|
||||
<sect>A78 header<p>
|
||||
|
||||
There is lots of different cart hardware available for the atari7800.
|
||||
Some carts have ROM, RAM, sound hardware, non-volatile high score chips.
|
||||
In order to know what kind of hardware the cart build requires there is
|
||||
a header file of 128 bytes in front of the binary.
|
||||
|
||||
The default build creates a cart file for a 48K rom cart without any
|
||||
extra features like the pokey audio chip or extra RAM.
|
||||
|
||||
In order to make cc65 more user friendly the build will add the a78
|
||||
header automatically. This allows you to run the binary on emulators
|
||||
and flash carts on the real console.
|
||||
|
||||
<sect>Encryption<p>
|
||||
|
||||
In order to boot the game in a mode that supports atari7800 functions
|
||||
the cart must be encrypted after the linking phase.
|
||||
There is a program called sign7800 that can be used to sign the cart.
|
||||
The encryption is not required for running the cart on emulators.
|
||||
You can also run atari2600 games without encryption.
|
||||
|
||||
<sect>Memory layout<p>
|
||||
|
||||
cc65 generated programs with the default setup can use RAM from
|
||||
from $1800 to $203f.
|
||||
The 4k RAM is then mapped to zero page area.
|
||||
$2040 to $20ff is visible as zero page.
|
||||
After that we have a vero small RAM area that is unused.
|
||||
$2100 to $213f.
|
||||
Then we mirror a second block from the RAM to become the hardware stack.
|
||||
This would be from $2140 to $21ff.
|
||||
|
||||
The C-stack starts at $2800 and it can grow down to $2200.
|
||||
|
||||
size of the system stack can be customized by defining the
|
||||
__STACKSIZE__ linker variable.
|
||||
|
||||
Special locations:
|
||||
|
||||
<descrip>
|
||||
<tag/Stack/ The C runtime stack is located at $2800 -
|
||||
__STACKSIZE__ and growing downwards.
|
||||
|
||||
<tag/Heap/ The C heap is located at $2200 and grows upwards.
|
||||
|
||||
</descrip><p>
|
||||
|
||||
<sect>Start-up condition<p>
|
||||
|
||||
When powered-up, the Atari 7800 TIA registers contain random
|
||||
values. During the initialization phase, the start-up code needs to
|
||||
initialize the TIA registers to sound values (or else the console has
|
||||
an unpredictable behavior). In this implementation, zeros are written
|
||||
to all of TIA registers during the start-up phase.
|
||||
|
||||
Note that RIOT registers (mostly timers) are left uninitialized, as
|
||||
they don't have any consequence on the console behavior.
|
||||
|
||||
<sect>Platform specific header files<p>
|
||||
|
||||
Programs containing Atari 7800 specific code may use the
|
||||
<tt/atari7800.h/ header file.
|
||||
|
||||
The following pseudo variables declared in the <tt/atari7800.h/ header
|
||||
file allow access to the Atari 7800 TIA, MARIA & RIOT chips registers.
|
||||
|
||||
<descrip>
|
||||
|
||||
<tag><tt/TIA/</tag> The <tt/TIA/ structure allows read/write access
|
||||
to the Atari 7800 TIA chip registers. See the <tt/_tia.h/ header
|
||||
file located in the include directory for the declaration of the
|
||||
structure. Also refer to the Stella Programmer's Guide by Steve
|
||||
Wright for a detailed description of the chip and its registers.
|
||||
|
||||
<tag><tt/RIOT/</tag> The <tt/RIOT/ structure allows read/write
|
||||
access to the Atari 7800 RIOT chip registers. See the
|
||||
<tt/_riot.h/ header file located in the include directory for the
|
||||
declaration of the structure. Also refer to the Stella Programmer's
|
||||
Guide by Steve Wright for a detailed description of the chip and its
|
||||
registers.
|
||||
|
||||
<tag><tt/MARIA/</tag> The <tt/MARIA/ structure allows read/write
|
||||
access to the Atari 7800 MARIA chip registers. See the
|
||||
<tt/_maria.h/ header file located in the include directory for the
|
||||
declaration of the structure.
|
||||
|
||||
</descrip><p>
|
||||
|
||||
|
||||
<sect>Loadable drivers<p>
|
||||
|
||||
There are no drivers for the Atari 7800.
|
||||
|
||||
|
||||
<sect>Limitations<p>
|
||||
|
||||
TBD
|
||||
|
||||
|
||||
<sect>Other hints<p>
|
||||
|
||||
One may write a custom linker configuration file to tune the memory
|
||||
layout of a program. See the <tt/atari7800.cfg/ file in the cfg
|
||||
directory as a starting point.
|
||||
|
||||
|
||||
<sect>License<p>
|
||||
|
||||
This software is provided 'as-is', without any expressed or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
<enum>
|
||||
<item> The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
<item> Altered source versions must be plainly marked as such, and must not
|
||||
be misrepresented as being the original software.
|
||||
<item> This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
</enum>
|
||||
|
||||
</article>
|
@ -126,7 +126,7 @@ and $FF3F.
|
||||
In memory constrained situations the memory from $400 to $7FF
|
||||
can be made available to a program by calling <tt/_heapadd ((void *) 0x0400, 0x0400);/
|
||||
at the beginning of <tt/main()/. Doing so is beneficial even if the program
|
||||
doesn't use the the heap explicitly because loading a driver uses the heap implicitly.
|
||||
doesn't use the heap explicitly because loading a driver uses the heap implicitly.
|
||||
|
||||
Using <tt/c64-soft80.o/ is as simple as placing it on the linker command
|
||||
line like this:
|
||||
|
@ -3108,7 +3108,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH
|
||||
<sect1><tt>.IFDEF</tt><label id=".IFDEF"><p>
|
||||
|
||||
Conditional assembly: Check if a symbol is defined. Must be followed by
|
||||
a symbol name. The condition is true if the the given symbol is already
|
||||
a symbol name. The condition is true if the given symbol is already
|
||||
defined, and false otherwise.
|
||||
|
||||
See also: <tt><ref id=".DEFINED" name=".DEFINED"></tt>
|
||||
@ -3143,7 +3143,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH
|
||||
<sect1><tt>.IFNDEF</tt><label id=".IFNDEF"><p>
|
||||
|
||||
Conditional assembly: Check if a symbol is defined. Must be followed by
|
||||
a symbol name. The condition is true if the the given symbol is not
|
||||
a symbol name. The condition is true if the given symbol is not
|
||||
defined, and false otherwise.
|
||||
|
||||
See also: <tt><ref id=".DEFINED" name=".DEFINED"></tt>
|
||||
@ -3152,7 +3152,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH
|
||||
<sect1><tt>.IFNREF</tt><label id=".IFNREF"><p>
|
||||
|
||||
Conditional assembly: Check if a symbol is referenced. Must be followed
|
||||
by a symbol name. The condition is true if if the the given symbol was
|
||||
by a symbol name. The condition is true if the given symbol was
|
||||
not referenced before, and false otherwise.
|
||||
|
||||
See also: <tt><ref id=".REFERENCED" name=".REFERENCED"></tt>
|
||||
@ -3197,7 +3197,7 @@ See: <tt><ref id=".ASCIIZ" name=".ASCIIZ"></tt>,<tt><ref id=".CHARMAP" name=".CH
|
||||
<sect1><tt>.IFREF</tt><label id=".IFREF"><p>
|
||||
|
||||
Conditional assembly: Check if a symbol is referenced. Must be followed
|
||||
by a symbol name. The condition is true if if the the given symbol was
|
||||
by a symbol name. The condition is true if the given symbol was
|
||||
referenced before, and false otherwise.
|
||||
|
||||
This command may be used to build subroutine libraries in include files
|
||||
@ -4763,6 +4763,7 @@ compiler, depending on the target system selected:
|
||||
<item><tt/__APPLE2ENH__/ - Target system is <tt/apple2enh/
|
||||
<item><tt/__ATARI2600__/ - Target system is <tt/atari2600/
|
||||
<item><tt/__ATARI5200__/ - Target system is <tt/atari5200/
|
||||
<item><tt/__ATARI7800__/ - Target system is <tt/atari7800/
|
||||
<item><tt/__ATARI__/ - Target system is <tt/atari/ or <tt/atarixl/
|
||||
<item><tt/__ATARIXL__/ - Target system is <tt/atarixl/
|
||||
<item><tt/__ATMOS__/ - Target system is <tt/atmos/
|
||||
|
183
doc/cc65.sgml
183
doc/cc65.sgml
@ -198,6 +198,189 @@ Here is a description of all the command line options:
|
||||
Enables debug mode, for debugging the behavior of cc65.
|
||||
|
||||
|
||||
<tag><tt>--debug-tables name</tt></tag>
|
||||
|
||||
Writes symbol table information to a file, which includes details on structs, unions
|
||||
functions, and global variables. For example, given the following code:
|
||||
|
||||
<tscreen><verb>
|
||||
struct l {
|
||||
unsigned char m;
|
||||
unsigned char n;
|
||||
};
|
||||
|
||||
struct hello {
|
||||
unsigned char j;
|
||||
unsigned char k;
|
||||
struct l l;
|
||||
};
|
||||
|
||||
struct sub {
|
||||
unsigned char x;
|
||||
unsigned char y;
|
||||
};
|
||||
|
||||
union xy {
|
||||
struct sub xy;
|
||||
unsigned int mem;
|
||||
};
|
||||
|
||||
typedef struct hello thingy;
|
||||
|
||||
unsigned char single;
|
||||
|
||||
unsigned char test_local_vars_main(void) {
|
||||
static unsigned char wahoo;
|
||||
static unsigned char bonanza = 0x42;
|
||||
unsigned char i;
|
||||
unsigned int j;
|
||||
unsigned int *random;
|
||||
unsigned char *lol;
|
||||
signed char whoa;
|
||||
struct hello wow;
|
||||
thingy *cool;
|
||||
union xy xy;
|
||||
|
||||
return 0;
|
||||
}
|
||||
</verb></tscreen>
|
||||
|
||||
The following output would be produced:
|
||||
|
||||
<tscreen><verb>
|
||||
SC_FUNC: _test_local_vars_main: Symbol table
|
||||
============================================
|
||||
__fixargs__:
|
||||
Flags: SC_CONST SC_DEF
|
||||
Type: unsigned int
|
||||
__argsize__:
|
||||
Flags: SC_CONST SC_DEF
|
||||
Type: unsigned char
|
||||
wahoo:
|
||||
AsmName: M0001
|
||||
Flags: SC_STATIC SC_DEF SC_REF
|
||||
Type: unsigned char
|
||||
bonanza:
|
||||
AsmName: M0002
|
||||
Flags: SC_STATIC SC_DEF SC_REF
|
||||
Type: unsigned char
|
||||
i:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: unsigned char
|
||||
j:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: unsigned int
|
||||
random:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: unsigned int *
|
||||
lol:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: unsigned char *
|
||||
whoa:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: signed char
|
||||
wow:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: struct hello
|
||||
cool:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: struct hello *
|
||||
xy:
|
||||
Flags: SC_AUTO SC_DEF SC_REF
|
||||
Type: union xy
|
||||
|
||||
|
||||
|
||||
|
||||
Global symbol table
|
||||
===================
|
||||
thingy:
|
||||
AsmName: _thingy
|
||||
Flags: SC_TYPEDEF 0x100000
|
||||
Type: struct hello
|
||||
single:
|
||||
AsmName: _single
|
||||
Flags: SC_STATIC SC_EXTERN SC_STORAGE SC_DEF SC_REF 0x100000
|
||||
Type: unsigned char
|
||||
test_local_vars_main:
|
||||
AsmName: _test_local_vars_main
|
||||
Flags: SC_FUNC SC_STATIC SC_EXTERN SC_DEF 0x100000
|
||||
Type: unsigned char (void)
|
||||
|
||||
|
||||
|
||||
|
||||
Global tag table
|
||||
================
|
||||
l:
|
||||
Flags: SC_STRUCT SC_DEF
|
||||
Type: (none)
|
||||
hello:
|
||||
Flags: SC_STRUCT SC_DEF
|
||||
Type: (none)
|
||||
sub:
|
||||
Flags: SC_STRUCT SC_DEF
|
||||
Type: (none)
|
||||
xy:
|
||||
Flags: SC_UNION SC_DEF
|
||||
Type: (none)
|
||||
|
||||
|
||||
|
||||
|
||||
Global struct and union definitions
|
||||
=========================
|
||||
|
||||
SC_STRUCT: l
|
||||
============
|
||||
m:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: unsigned char
|
||||
n:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: unsigned char
|
||||
|
||||
|
||||
|
||||
|
||||
SC_STRUCT: hello
|
||||
================
|
||||
j:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: unsigned char
|
||||
k:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: unsigned char
|
||||
l:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: struct l
|
||||
|
||||
|
||||
|
||||
|
||||
SC_STRUCT: sub
|
||||
==============
|
||||
x:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: unsigned char
|
||||
y:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: unsigned char
|
||||
|
||||
|
||||
|
||||
|
||||
SC_UNION: xy
|
||||
============
|
||||
xy:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: struct sub
|
||||
mem:
|
||||
Flags: SC_STRUCTFIELD
|
||||
Type: unsigned int
|
||||
</verb></tscreen>
|
||||
|
||||
|
||||
<tag><tt>--debug-opt name</tt></tag>
|
||||
|
||||
The named file contains a list of specific optimization steps to enable or disable.
|
||||
|
@ -6127,7 +6127,7 @@ pointer you're passing somewhere else, otherwise
|
||||
<tscreen><verb>
|
||||
ptr = realloc (ptr, size);
|
||||
</verb></tscreen>
|
||||
will loose your only copy of <tt/ptr/ if <tt/realloc/ returns <tt/NULL/.
|
||||
will lose your only copy of <tt/ptr/ if <tt/realloc/ returns <tt/NULL/.
|
||||
<item>The function is only available as fastcall function, so it may only
|
||||
be used in presence of a prototype.
|
||||
</itemize>
|
||||
|
@ -124,6 +124,9 @@
|
||||
<tag><htmlurl url="atari5200.html" name="atari5200.html"></tag>
|
||||
Topics specific to the Atari 5200 Game Console.
|
||||
|
||||
<tag><htmlurl url="atari7800.html" name="atari7800.html"></tag>
|
||||
Topics specific to the Atari 7800 Game Console.
|
||||
|
||||
<tag><htmlurl url="atmos.html" name="atmos.html"></tag>
|
||||
Topics specific to the Oric Atmos.
|
||||
|
||||
|
@ -500,7 +500,7 @@ prompt.
|
||||
|
||||
Before you can run the cartridge image produced by the linker, the binary has to
|
||||
be patched using the <bf/gamate-fixcart/ tool that is included in the cc65
|
||||
package in the util/gamata directory.
|
||||
package in the util/gamate/ directory.
|
||||
|
||||
<tscreen><verb>
|
||||
gamate-fixcart <image.bin>
|
||||
|
@ -166,6 +166,7 @@ Here is a description of all of the command-line options:
|
||||
<item>apple2
|
||||
<item>apple2enh
|
||||
<item>atari2600
|
||||
<item>atari7800
|
||||
<item>atari
|
||||
<item>atarixl
|
||||
<item>atmos
|
||||
|
@ -15,21 +15,40 @@ An overview over the Sym-1 runtime system as it is implemented for the cc65 C co
|
||||
|
||||
<sect>Overview<p>
|
||||
|
||||
This file contains an overview of the Sym-1 runtime system as it comes with the cc65 C compiler. It describes the memory layout, Sym-1 specific header files, available drivers, and any pitfalls specific to the platform.
|
||||
This file contains an overview of the Sym-1 runtime system as it comes with the cc65 C compiler.
|
||||
It describes the memory layout, Sym-1 specific header files, available drivers, and any pitfalls
|
||||
specific to the platform.
|
||||
|
||||
Please note that Sym-1 specific functions are just mentioned here, they are described in detail in the separate <url url="funcref.html" name="function reference">. Even functions marked as "platform dependent" may be available on more than one platform. Please see the function reference for more information.
|
||||
Please note that Sym-1 specific functions are just mentioned here, they are described in detail
|
||||
in the separate <url url="funcref.html" name="function reference">. Even functions marked as
|
||||
"platform dependent" may be available on more than one platform. Please see the
|
||||
function reference for more information.
|
||||
|
||||
<sect>Binary format<p>
|
||||
|
||||
The output format generated by the linker for the Sym-1 target is a raw binary BIN file, which is essentially a memory image. You can convert this to a HEX file using BIN2HEX, which is a popular open-source conversion utility program. A HEX file has ASCII representations of the hexadecimal byte values of the machine-language program. So the HEX file can be transferred to the Sym-1 using the RS-232 terminal port, just as if the machine-code was entered by hand. Enter 'm 200' in the monitor and start the HEX file transfer.
|
||||
The output format generated by the linker for the Sym-1 target is a raw binary BIN file, which
|
||||
is essentially a memory image. You can convert this to a HEX file using BIN2HEX, which is a
|
||||
popular open-source conversion utility program. A HEX file has ASCII representations of the
|
||||
hexadecimal byte values of the machine-language program. So the HEX file can be transferred
|
||||
to the Sym-1 using the RS-232 terminal port, just as if the machine-code was entered by hand.
|
||||
Enter 'm 200' in the monitor and start the HEX file transfer.
|
||||
|
||||
<p>
|
||||
|
||||
Included with this distribution is a 4k configuration file and a 32k config file. The Sym-1 on-board memory is limited to 4 kbytes but system memory can be increased to 32 kbytes of contiguous RAM with aftermarket add-on boards. So choose the config file that matches your system configuration before compiling and linking user programs.
|
||||
Included with this distribution is a 4k configuration file and a 32k config file. The Sym-1
|
||||
on-board memory is limited to 4 kbytes but system memory can be increased to 32 kbytes of
|
||||
contiguous RAM with aftermarket add-on boards. So choose the config file that matches your
|
||||
system configuration before compiling and linking user programs.
|
||||
|
||||
<sect>Memory layout<p>
|
||||
|
||||
The ROMs and I/O areas are defined in the configuration files, as are most of the entry points for useful subroutines in the Sym-1 monitor ROM. cc65 generated programs compiled and linked using 4k config run in the memory range of $200 - $0FFF. The 32k config expands this range to $7FFF. The starting memory location and entry point for running the program is $200, so when the program is transferred to the Sym-1, it is executed by typing 'g 200'. The system returns control back to the monitor ROM when the program terminates, providing the '.' prompt.
|
||||
The ROMs and I/O areas are defined in the configuration files, as are most of the entry points
|
||||
for useful subroutines in the Sym-1 monitor ROM. cc65 generated programs compiled and linked
|
||||
using 4k config run in the memory range of $200 - $0FFF. The 32k config expands
|
||||
this range to $7FFF. Memory above 32k can be used to extend the heap, as described below.
|
||||
The starting memory location and entry point for running the program is $200, so when the
|
||||
program is transferred to the Sym-1, it is executed by typing 'g 200'. The system returns control
|
||||
back to the monitor ROM when the program terminates, providing the '.' prompt.
|
||||
|
||||
Special locations:
|
||||
|
||||
@ -38,10 +57,12 @@ Special locations:
|
||||
Conio support is not currently available for the Sym-1. But stdio console functions are available.
|
||||
|
||||
<tag/Stack/
|
||||
The C runtime stack is located at $0FFF on 4KB Syms, or at $7FFF for 32KB systems. The stack always grows downwards.
|
||||
The C runtime stack is located at $0FFF on 4kb Syms, or at $7FFF for 32kb systems.
|
||||
The stack always grows downwards.
|
||||
|
||||
<tag/Heap/
|
||||
The C heap is located at the end of the program and grows towards the C runtime stack.
|
||||
The C heap is located at the end of the program and grows towards the C runtime stack. Extended
|
||||
memory can be added to the heap, as described below.
|
||||
|
||||
</descrip><p>
|
||||
|
||||
@ -51,7 +72,8 @@ Programs containing Sym-1 code may use the <tt/sym1.h/ header file. See the hea
|
||||
|
||||
<sect1>Hardware access<p>
|
||||
|
||||
The pseudo variables declared in the <tt/sym1.inc/ include file allow access to hardware located in the address space. See the include file for more information.
|
||||
The pseudo variables declared in the <tt/sym1.inc/ include file allow access to hardware located in the
|
||||
address space. See the include file for more information.
|
||||
|
||||
<sect>Loadable drivers<p>
|
||||
|
||||
@ -61,7 +83,9 @@ No graphics drivers are currently available for the Sym-1.
|
||||
|
||||
<sect1>Extended memory drivers<p>
|
||||
|
||||
No extended memory drivers are currently available for the Sym-1.
|
||||
There are no extended memory drivers for the Sym-1. However, there is a way to access memory beyond the
|
||||
32kb boundary, if extended memory is physically present in the system. See the example program,
|
||||
symExtendedMemory, in the samples directory.
|
||||
|
||||
<sect1>Joystick drivers<p>
|
||||
|
||||
@ -73,7 +97,8 @@ No mouse drivers are currently available for the Sym-1.
|
||||
|
||||
<sect1>RS232 device drivers<p>
|
||||
|
||||
No communication port drivers are currently available for the Sym-1. It has only the "master console" e.g. stdin and stdout.
|
||||
No communication port drivers are currently available for the Sym-1. It has only the "master console"
|
||||
e.g. stdin and stdout.
|
||||
|
||||
<sect>Limitations<p>
|
||||
|
||||
@ -94,29 +119,45 @@ To be more specific, this limitation means that you cannot use any of the follow
|
||||
<sect>Other hints<p>
|
||||
|
||||
<sect1>sym1.h<p>
|
||||
This header exposes Sym-specific I/O functions that are useful for reading and writing its ports and front panel. See the <tt/sym1.h/ include file for a list of the functions available.
|
||||
This header exposes Sym-specific I/O functions that are useful for reading and writing its ports and front panel.
|
||||
See the <tt/sym1.h/ include file for a list of the functions available.
|
||||
|
||||
<sect2>Limited memory applications<p>
|
||||
|
||||
As stated earlier, there are config files for 4KB and 32KB systems. If you have 32KB RAM, then you will probably want to use the sym1-32k configuration, but if not - if you are using the sym1-4k configuration - then you may want to use functions like getchar, putchar, gets and puts rather than functions like scanf and printf. Printf, for example, requires about 1KB because it needs to know how to process all the format specifiers.
|
||||
As stated earlier, there are config files for 4kb and 32kb systems. If you have 32kb RAM, then you will probably
|
||||
want to use the sym1-32k configuration, but if not - if you are using the sym1-4k configuration - then you may
|
||||
want to use functions like getchar, putchar, gets and puts rather than functions like scanf and printf.
|
||||
Printf, for example, requires about 1KB because it needs to know how to process all the format specifiers.
|
||||
|
||||
<sect3>Sample programs<p>
|
||||
<sect3>Using extended memory<p>
|
||||
|
||||
All the samples will run on the "stock" 4KB Sym-1, except for symIO and symNotepad, which require 32KB. These sample programs can be found in the samples/sym1 directory:
|
||||
Memory may be physically present that is addressed at locations above the monitor ROM at $8000. This extended
|
||||
memory is accessible by adding to the heap, as described in the symExtendedMemory sample program.
|
||||
|
||||
<sect4>Sample programs<p>
|
||||
|
||||
All the samples will run on the "stock" 4kb Sym-1, except for symIO and symNotepad, which require 32kb.
|
||||
Additionally, symExtendedMemory shows how to access memory above 32kb, so it expects more than 32kb.
|
||||
These sample programs can be found in the samples/sym1 directory:
|
||||
|
||||
<itemize>
|
||||
<item>symHello prints "Hello World!" and then inputs characters, which are echoed on the screen. It also makes a "beep" sound.</item>
|
||||
<item>symTiny does the same as symHello, but does it with puts() rather than printf() to show the difference in compiled binary size.</item>
|
||||
<item>symHello prints "Hello World!" and then inputs characters, which are echoed on the screen.
|
||||
It also makes a "beep" sound.</item>
|
||||
<item>symTiny does the same as symHello, but does it with puts() rather than printf() to show the difference
|
||||
in compiled binary size.</item>
|
||||
<item>symDisplay allows entry of a message, which is then displayed by scrolling it across the front panel display.</item>
|
||||
<item>symIO allows access to the Sym-1 digital I/O ports.</item>
|
||||
<item>symNotepad is a simple text entry/retrieval program that uses tape storage.</item>
|
||||
<item>symExtendedMemory demonstrates how to access upper-memory and add it to the heap.</item>
|
||||
</itemize>
|
||||
|
||||
<sect>License<p>
|
||||
|
||||
This software is provided 'as-is', without any expressed or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
This software is provided 'as-is', without any expressed or implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter
|
||||
it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
<enum>
|
||||
<item> The origin of this software must not be misrepresented; you must not
|
||||
|
@ -76,13 +76,13 @@ ifneq ($(MAKECMDGOALS),clean)
|
||||
endif
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) -o $@ $<
|
||||
	$(CC) -c $(CFLAGS) -o $@ $<
|
||||
|
||||
$(PROGRAM): $(SOURCES:.c=.o)
|
||||
$(CC) $(LDFLAGS) -o $@ $^
|
||||
	$(CC) $(LDFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) $(SOURCES:.c=.o) $(SOURCES:.c=.d) $(PROGRAM) $(PROGRAM).map
|
||||
	$(RM) $(SOURCES:.c=.o) $(SOURCES:.c=.d) $(PROGRAM) $(PROGRAM).map
|
||||
</verb></tscreen>
|
||||
|
||||
<bf/Important:/ When using the sample Makefile above via copy & paste it is
|
||||
|
@ -105,7 +105,7 @@ struct __dos2x {
|
||||
unsigned char* zbufp; /* points to user filename */
|
||||
unsigned char* zdrva; /* points to serveral buffers (mostly VTOC) */
|
||||
unsigned char* zsba; /* points to sector buffer */
|
||||
unsigned char errno; /* number of occured error */
|
||||
unsigned char errno; /* number of occurred error */
|
||||
};
|
||||
|
||||
typedef struct __dos2x dos2x_t;
|
||||
|
63
include/_maria.h
Normal file
63
include/_maria.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* _maria.h */
|
||||
/* */
|
||||
/* Atari 7800, Maria chip register hardware structures */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
/*
|
||||
* MARIA registers
|
||||
*/
|
||||
struct __maria {
|
||||
unsigned char bkgrnd;
|
||||
unsigned char p0c1;
|
||||
unsigned char p0c2;
|
||||
unsigned char p0c3;
|
||||
unsigned char wsync;
|
||||
unsigned char p1c1;
|
||||
unsigned char p1c2;
|
||||
unsigned char p1c3;
|
||||
unsigned char mstat;
|
||||
unsigned char p2c1;
|
||||
unsigned char p2c2;
|
||||
unsigned char p2c3;
|
||||
unsigned char dpph;
|
||||
unsigned char p3c1;
|
||||
unsigned char p3c2;
|
||||
unsigned char p3c3;
|
||||
unsigned char dppl;
|
||||
unsigned char p4c1;
|
||||
unsigned char p4c2;
|
||||
unsigned char p4c3;
|
||||
unsigned char chbase;
|
||||
unsigned char p5c1;
|
||||
unsigned char p5c2;
|
||||
unsigned char p5c3;
|
||||
unsigned char offset;
|
||||
unsigned char p6c1;
|
||||
unsigned char p6c2;
|
||||
unsigned char p6c3;
|
||||
unsigned char ctrl;
|
||||
unsigned char p7c1;
|
||||
unsigned char p7c2;
|
||||
unsigned char p7c3;
|
||||
};
|
65
include/atari7800.h
Normal file
65
include/atari7800.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* Atari VCS 7800 TIA & RIOT registers addresses */
|
||||
/* */
|
||||
/* Karri Kaksonen (karri@sipo.fi), 2022 */
|
||||
/* */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#ifndef _ATARI7800_H
|
||||
#define _ATARI7800_H
|
||||
|
||||
/* Check for errors */
|
||||
#if !defined(__ATARI7800__)
|
||||
# error This module may only be used when compiling for the Atari 7800!
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Color defines */
|
||||
#define COLOR_BLACK 0x00
|
||||
#define COLOR_GREY 0x01
|
||||
#define COLOR_LIGHTGREY 0x02
|
||||
#define COLOR_WHITE 0x03
|
||||
|
||||
/* TGI color defines (default palette) */
|
||||
#define TGI_COLOR_BLACK COLOR_BLACK
|
||||
#define TGI_COLOR_GREY COLOR_GREY
|
||||
#define TGI_COLOR_LIGHTGREY COLOR_LIGHTGREY
|
||||
#define TGI_COLOR_WHITE COLOR_WHITE
|
||||
|
||||
/* Masks for joy_read */
|
||||
#define JOY_RIGHT_MASK 0x80
|
||||
#define JOY_LEFT_MASK 0x40
|
||||
#define JOY_DOWN_MASK 0x20
|
||||
#define JOY_UP_MASK 0x10
|
||||
#define JOY_BTN_1_MASK 0x01
|
||||
#define JOY_BTN_2_MASK 0x02
|
||||
|
||||
#define JOY_BTN_A_MASK JOY_BTN_1_MASK
|
||||
#define JOY_BTN_B_MASK JOY_BTN_2_MASK
|
||||
|
||||
#define JOY_BTN_A(v) ((v) & JOY_BTN_A_MASK)
|
||||
#define JOY_BTN_B(v) ((v) & JOY_BTN_B_MASK)
|
||||
|
||||
/* No support for dynamically loadable drivers */
|
||||
#define DYN_DRV 0
|
||||
|
||||
#include <_tia.h>
|
||||
#define TIA (*(struct __tia*)0x0000)
|
||||
|
||||
#include <_riot.h>
|
||||
#define RIOT (*(struct __riot*)0x0280)
|
||||
|
||||
#include <_maria.h>
|
||||
#define MARIA (*(struct __maria*)0x0020)
|
||||
|
||||
/* End of atari7800.h */
|
||||
#endif
|
@ -59,21 +59,21 @@ unsigned long __fastcall__ udiv32by16r16 (unsigned long rhs, unsigned lhs);
|
||||
*/
|
||||
|
||||
int __fastcall__ imul8x8r16 (signed char lhs, signed char rhs);
|
||||
/* Multiplicate two signed 8 bit to yield an signed 16 bit result */
|
||||
/* Multiply two signed 8 bit to yield an signed 16 bit result */
|
||||
|
||||
long __fastcall__ imul16x16r32 (int lhs, int rhs);
|
||||
/* Multiplicate two signed 16 bit to yield a signed 32 bit result */
|
||||
/* Multiply two signed 16 bit to yield a signed 32 bit result */
|
||||
|
||||
unsigned __fastcall__ umul8x8r16 (unsigned char lhs, unsigned char rhs);
|
||||
/* Multiplicate two unsigned 8 bit to yield an unsigned 16 bit result */
|
||||
/* Multiply two unsigned 8 bit to yield an unsigned 16 bit result */
|
||||
|
||||
unsigned long __fastcall__ umul16x8r32 (unsigned lhs, unsigned char rhs);
|
||||
/* Multiplicate an unsigned 16 bit by an unsigned 8 bit number yielding a 24
|
||||
/* Multiply an unsigned 16 bit by an unsigned 8 bit number yielding a 24
|
||||
** bit unsigned result that is extended to 32 bits for easier handling from C.
|
||||
*/
|
||||
|
||||
unsigned long __fastcall__ umul16x16r32 (unsigned lhs, unsigned rhs);
|
||||
/* Multiplicate two unsigned 16 bit to yield an unsigned 32 bit result */
|
||||
/* Multiply two unsigned 16 bit to yield an unsigned 32 bit result */
|
||||
|
||||
unsigned int __fastcall__ mul20 (unsigned char value);
|
||||
/* Multiply an 8 bit unsigned value by 20 and return the 16 bit unsigned
|
||||
|
@ -145,6 +145,8 @@
|
||||
|
||||
/* constants for the conio implementation */
|
||||
#define COLOR_BLACK 0x03
|
||||
#define COLOR_GRAY2 0x02
|
||||
#define COLOR_GRAY1 0x01
|
||||
#define COLOR_WHITE 0x00
|
||||
|
||||
#define CH_HLINE 1
|
||||
|
@ -52,24 +52,25 @@
|
||||
|
||||
|
||||
/* Color defines */
|
||||
#define COLOR_BLACK 0x00
|
||||
#define COLOR_RED 0x01
|
||||
#define COLOR_PINK 0x02
|
||||
#define COLOR_LIGHTGREY 0x03
|
||||
#define COLOR_GREY 0x04
|
||||
#define COLOR_DARKGREY 0x05
|
||||
#define COLOR_BROWN 0x06
|
||||
#define COLOR_PEACH 0x07
|
||||
#define COLOR_YELLOW 0x08
|
||||
#define COLOR_LIGHTGREEN 0x09
|
||||
#define COLOR_GREEN 0x0A
|
||||
#define COLOR_DARKBROWN 0x0B
|
||||
#define COLOR_TRANSPARENT 0x00
|
||||
#define COLOR_BLACK 0x01
|
||||
#define COLOR_RED 0x02
|
||||
#define COLOR_PINK 0x03
|
||||
#define COLOR_LIGHTGREY 0x04
|
||||
#define COLOR_GREY 0x05
|
||||
#define COLOR_DARKGREY 0x06
|
||||
#define COLOR_BROWN 0x07
|
||||
#define COLOR_PEACH 0x08
|
||||
#define COLOR_YELLOW 0x09
|
||||
#define COLOR_LIGHTGREEN 0x0A
|
||||
#define COLOR_GREEN 0x0B
|
||||
#define COLOR_VIOLET 0x0C
|
||||
#define COLOR_BLUE 0x0D
|
||||
#define COLOR_LIGHTBLUE 0x0E
|
||||
#define COLOR_WHITE 0x0F
|
||||
|
||||
/* TGI color defines (default palette) */
|
||||
#define TGI_COLOR_TRANSPARENT COLOR_TRANSPARENT
|
||||
#define TGI_COLOR_BLACK COLOR_BLACK
|
||||
#define TGI_COLOR_RED COLOR_RED
|
||||
#define TGI_COLOR_PINK COLOR_PINK
|
||||
@ -81,7 +82,6 @@
|
||||
#define TGI_COLOR_YELLOW COLOR_YELLOW
|
||||
#define TGI_COLOR_LIGHTGREEN COLOR_LIGHTGREEN
|
||||
#define TGI_COLOR_GREEN COLOR_GREEN
|
||||
#define TGI_COLOR_DARKBROWN COLOR_DARKBROWN
|
||||
#define TGI_COLOR_VIOLET COLOR_VIOLET
|
||||
#define TGI_COLOR_BLUE COLOR_BLUE
|
||||
#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE
|
||||
|
@ -43,6 +43,8 @@
|
||||
# include <atari2600.h>
|
||||
#elif defined(__ATARI5200__)
|
||||
# include <atari5200.h>
|
||||
#elif defined(__ATARI7800__)
|
||||
# include <atari7800.h>
|
||||
#elif defined(__ATMOS__)
|
||||
# include <atmos.h>
|
||||
#elif defined(__CBM__)
|
||||
|
@ -21,6 +21,7 @@ TARGETS = apple2 \
|
||||
atarixl \
|
||||
atari2600 \
|
||||
atari5200 \
|
||||
atari7800 \
|
||||
atmos \
|
||||
creativision \
|
||||
$(CBMS) \
|
||||
|
@ -129,7 +129,7 @@ setbuf: lda #$00 ; Low byte
|
||||
.assert MLI::OPEN::PATHNAME = MLI::INFO::PATHNAME, error
|
||||
|
||||
; Lower file level to avoid program file
|
||||
; being closed by C libary shutdown code
|
||||
; being closed by C library shutdown code
|
||||
ldx LEVEL
|
||||
stx level
|
||||
beq :+
|
||||
@ -185,13 +185,13 @@ setbuf: lda #$00 ; Low byte
|
||||
lda #$00 ; '\0'
|
||||
beq :- ; Branch always
|
||||
|
||||
; Call loader stub after C libary shutdown
|
||||
; Call loader stub after C library shutdown
|
||||
: lda #<target
|
||||
ldx #>target
|
||||
sta done+1
|
||||
stx done+2
|
||||
|
||||
; Initiate C libary shutdown
|
||||
; Initiate C library shutdown
|
||||
jmp _exit
|
||||
|
||||
.bss
|
||||
|
69
libsrc/atari7800/clock.s
Normal file
69
libsrc/atari7800/clock.s
Normal file
@ -0,0 +1,69 @@
|
||||
;
|
||||
; 2022-03-15, Karri Kaksonen
|
||||
;
|
||||
; clock_t clock (void);
|
||||
;
|
||||
|
||||
.export _clock, clock_count
|
||||
.interruptor update_clock, 2 ; (low priority)
|
||||
.constructor init_clock
|
||||
|
||||
.import sreg: zp
|
||||
.import _zonecounter
|
||||
.include "atari7800.inc"
|
||||
|
||||
.macpack generic
|
||||
|
||||
.code
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Read the clock counter.
|
||||
;
|
||||
.proc _clock
|
||||
|
||||
lda #0
|
||||
sta sreg+1 ; Promote 24 bits up to 32 bits
|
||||
lda clock_count+2
|
||||
sta sreg
|
||||
ldx clock_count+1
|
||||
lda clock_count
|
||||
|
||||
rts
|
||||
.endproc
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; This interrupt handler increments a 24-bit counter at every video
|
||||
; vertical-blanking time.
|
||||
; Update the clock only on interrupt while the drawing on screen is on
|
||||
; _zonecounter == 1 (from 1st visible scanline to last visible scanline)
|
||||
;
|
||||
update_clock:
|
||||
lda _zonecounter
|
||||
and #01
|
||||
beq @L1
|
||||
inc clock_count
|
||||
bne @L1
|
||||
inc clock_count+1
|
||||
bne @L1
|
||||
inc clock_count+2
|
||||
@L1: ;clc ; General interrupt was not reset
|
||||
rts
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Set time to zero at startup
|
||||
;
|
||||
.segment "ONCE"
|
||||
init_clock:
|
||||
lda #0
|
||||
sta clock_count+2
|
||||
sta clock_count+1
|
||||
sta clock_count
|
||||
rts
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Store time in 3 bytes
|
||||
;
|
||||
.bss
|
||||
clock_count:
|
||||
.res 3
|
||||
|
34
libsrc/atari7800/clocks_per_sec.s
Normal file
34
libsrc/atari7800/clocks_per_sec.s
Normal file
@ -0,0 +1,34 @@
|
||||
;
|
||||
; 2022-03-15, Karri Kaksonen
|
||||
;
|
||||
; clock_t _clocks_per_sec (void);
|
||||
;
|
||||
|
||||
.export __clocks_per_sec
|
||||
|
||||
.import sreg: zp
|
||||
.import _paldetected
|
||||
.include "atari7800.inc"
|
||||
|
||||
.macpack generic
|
||||
|
||||
.code
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Return the number of clock ticks in one second.
|
||||
;
|
||||
.proc __clocks_per_sec
|
||||
|
||||
lda #0
|
||||
tax
|
||||
sta sreg ; return 32 bits
|
||||
sta sreg+1
|
||||
lda _paldetected
|
||||
bne pal
|
||||
lda #60 ; NTSC - 60Hz
|
||||
rts
|
||||
pal:
|
||||
lda #50 ; PAL - 50Hz
|
||||
rts
|
||||
.endproc
|
||||
|
71
libsrc/atari7800/crt0.s
Normal file
71
libsrc/atari7800/crt0.s
Normal file
@ -0,0 +1,71 @@
|
||||
.export _zonecounter
|
||||
.export __STARTUP__ : absolute = 1
|
||||
.export _exit
|
||||
.import __ROM_START__
|
||||
.import __RAM3_START__, __RAM3_SIZE__
|
||||
.import initlib, donelib
|
||||
.import zerobss, copydata
|
||||
.import IRQStub
|
||||
.import push0, _main
|
||||
.include "atari7800.inc"
|
||||
.include "zeropage.inc"
|
||||
|
||||
INPTCTRL = $01
|
||||
|
||||
.segment "STARTUP"
|
||||
start:
|
||||
; Startup sequence recommended by Atari.
|
||||
; See the 7800 standards document.
|
||||
sei ; Initialize 6502
|
||||
cld
|
||||
lda #$07 ; Lock machine in 7800 mode
|
||||
sta INPTCTRL
|
||||
lda #$7f ; DMA off
|
||||
sta CTRL
|
||||
ldx #0 ; OFFSET must always be 0
|
||||
stx OFFSET
|
||||
stx INPTCTRL ; Make sure joysticks don't freeze
|
||||
dex ; Stack pointer = $ff
|
||||
txs
|
||||
|
||||
; Set up parameter stack
|
||||
lda #<(__RAM3_START__ + __RAM3_SIZE__)
|
||||
sta sp
|
||||
lda #>(__RAM3_START__ + __RAM3_SIZE__)
|
||||
sta sp+1
|
||||
|
||||
jsr copydata
|
||||
jsr zerobss
|
||||
jsr initlib
|
||||
|
||||
; Call main program (pass empty command line)
|
||||
jsr push0 ; argc
|
||||
jsr push0 ; argv
|
||||
ldy #4 ; Argument size
|
||||
jsr _main
|
||||
|
||||
_exit:
|
||||
jsr donelib
|
||||
jmp start
|
||||
|
||||
NMIHandler:
|
||||
inc _zonecounter
|
||||
jmp IRQStub
|
||||
|
||||
IRQHandler:
|
||||
rti
|
||||
|
||||
.segment "DATA"
|
||||
_zonecounter:
|
||||
.byte 0
|
||||
|
||||
.segment "ENCRYPTION"
|
||||
.res 126, $ff ; Reserved for encryption
|
||||
Lfff8: .byte $ff ; Region verification (always $ff)
|
||||
Lfff9: .byte $f7 ; Use last 4096 bytes only for encryption
|
||||
;;;Lfff9: .byte <(((__ROM_START__/4096)<<4) | 7)
|
||||
|
||||
.segment "VECTORS"
|
||||
.word NMIHandler
|
||||
.word start
|
||||
.word IRQHandler
|
5
libsrc/atari7800/ctype.s
Normal file
5
libsrc/atari7800/ctype.s
Normal file
@ -0,0 +1,5 @@
|
||||
; Character specification table.
|
||||
;
|
||||
; uses the "common" definition
|
||||
|
||||
.include "ctype_common.inc"
|
46
libsrc/atari7800/exehdr.s
Normal file
46
libsrc/atari7800/exehdr.s
Normal file
@ -0,0 +1,46 @@
|
||||
;
|
||||
; Karri Kaksonen, 2022
|
||||
;
|
||||
; This header contains data for emulators
|
||||
;
|
||||
.export __EXEHDR__: absolute = 1
|
||||
.import __CARTSIZE__
|
||||
; ------------------------------------------------------------------------
|
||||
; EXE header
|
||||
.segment "EXEHDR"
|
||||
.byte 3 ; version
|
||||
.byte 'A','T','A','R','I','7','8','0','0',' ',' ',' ',' ',' ',' ',' '
|
||||
.byte 'G','a','m','e',' ','n','a','m','e',0,0,0,0,0,0,0
|
||||
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
.byte 0,0,>__CARTSIZE__,0 ; Set the cart size in the cfg file
|
||||
; bit 0 - pokey at 4000
|
||||
; bit 1 - supergame bank switched
|
||||
; bit 2 - supergame ram at $4000
|
||||
; bit 3 - rom at $4000
|
||||
; bit 4 - bank 6 at $4000
|
||||
; bit 5 - supergame banked ram
|
||||
; bit 6 - pokey at $450
|
||||
; bit 7 - mirror ram at $4000
|
||||
; bit 8 - activision banking
|
||||
; bit 9 - absolute banking
|
||||
; bit 10 - pokey at $440
|
||||
; bit 11 - ym2151 at $461/462
|
||||
; bit 12 - souper
|
||||
; bit 13-15 - Special
|
||||
; 0 = Normal cart
|
||||
.byte 0,0 ; 0 = Normal cart
|
||||
.byte 1 ; 1 = Joystick, 2 = lightgun
|
||||
.byte 0 ; No joystick 2
|
||||
.byte 0 ; bit0 = 0:NTSC,1:PAL bit1 = 0:component,1:composite
|
||||
.byte 0 ; Save data peripheral - 1 byte (version 2)
|
||||
; 0 = None / unknown (default)
|
||||
; 1 = High Score Cart (HSC)
|
||||
; 2 = SaveKey
|
||||
|
||||
.byte 0 ; 63 Expansion module
|
||||
; 0 = No expansion module (default on all currently released games)
|
||||
; 1 = Expansion module required
|
||||
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
.byte 0,0,0,0,0,0,0,0
|
||||
.byte 'A','C','T','U','A','L',' ','C','A','R','T',' ','D','A','T','A',' ','S','T','A','R','T','S',' ','H','E','R','E'
|
65
libsrc/atari7800/get_tv.s
Normal file
65
libsrc/atari7800/get_tv.s
Normal file
@ -0,0 +1,65 @@
|
||||
;
|
||||
; Karri Kaksonen, 2022-03-25
|
||||
;
|
||||
; unsigned char get_tv (void)
|
||||
;
|
||||
.include "atari7800.inc"
|
||||
.include "get_tv.inc"
|
||||
.export _get_tv
|
||||
.export _paldetected
|
||||
|
||||
.segment "DATA"
|
||||
|
||||
_paldetected:
|
||||
.byte $FF
|
||||
|
||||
; ---------------------------------------------------------------
|
||||
; unsigned char get_tv (void)
|
||||
; ---------------------------------------------------------------
|
||||
|
||||
.segment "CODE"
|
||||
|
||||
.proc _get_tv: near
|
||||
|
||||
.segment "CODE"
|
||||
|
||||
ldx #$00
|
||||
lda #$FF
|
||||
cmp _paldetected
|
||||
bne L8
|
||||
L1: lda MSTAT
|
||||
and #$80
|
||||
bne L1
|
||||
L2: lda MSTAT
|
||||
and #$80
|
||||
beq L2
|
||||
L3: lda MSTAT
|
||||
and #$80
|
||||
bne L3
|
||||
lda #$00
|
||||
sta M0001
|
||||
jmp L5
|
||||
L4: sta MWSYNC
|
||||
sta MWSYNC
|
||||
dec M0001
|
||||
L5: lda MSTAT
|
||||
and #$80
|
||||
beq L4
|
||||
lda M0001
|
||||
cmp #$78
|
||||
bcc L6
|
||||
lda #TV::NTSC
|
||||
jmp L7
|
||||
L6: lda #TV::PAL
|
||||
L7: sta _paldetected
|
||||
ldx #$00
|
||||
L8: lda _paldetected
|
||||
rts
|
||||
|
||||
.segment "BSS"
|
||||
|
||||
M0001:
|
||||
.res 1,$00
|
||||
|
||||
.endproc
|
||||
|
36
libsrc/atari7800/irq.s
Normal file
36
libsrc/atari7800/irq.s
Normal file
@ -0,0 +1,36 @@
|
||||
;
|
||||
; IRQ handling (Atari 7800 version)
|
||||
;
|
||||
|
||||
.export initirq, doneirq, IRQStub
|
||||
|
||||
.import __INTERRUPTOR_COUNT__, callirq
|
||||
|
||||
.include "atari7800.inc"
|
||||
|
||||
.code
|
||||
; ------------------------------------------------------------------------
|
||||
|
||||
initirq:
|
||||
doneirq:
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
|
||||
IRQStub:
|
||||
cld ; Just to be sure
|
||||
pha
|
||||
lda #<(__INTERRUPTOR_COUNT__ * 2)
|
||||
beq @L1
|
||||
txa
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
jsr callirq ; Call the functions
|
||||
pla
|
||||
tay
|
||||
pla
|
||||
tax
|
||||
@L1: pla
|
||||
rti
|
||||
|
161
libsrc/atari7800/joy/atari7800-stdjoy.s
Normal file
161
libsrc/atari7800/joy/atari7800-stdjoy.s
Normal file
@ -0,0 +1,161 @@
|
||||
;
|
||||
; Standard joystick driver for the Atari 7800.
|
||||
; This version tries to use 7800 and 2600 joysticks.
|
||||
; But assumes that both joysticks are of same type.
|
||||
;
|
||||
; Modified by Karri Kaksonen, 2022-03-31
|
||||
; Ullrich von Bassewitz, 2002-12-20
|
||||
; Using code from Steve Schmidtke
|
||||
;
|
||||
|
||||
.include "zeropage.inc"
|
||||
|
||||
.include "joy-kernel.inc"
|
||||
.include "joy-error.inc"
|
||||
.include "atari7800.inc"
|
||||
|
||||
.macpack generic
|
||||
.macpack module
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; Header. Includes jump table
|
||||
|
||||
module_header _atari7800_stdjoy_joy
|
||||
|
||||
; Driver signature
|
||||
|
||||
.byte $6A, $6F, $79 ; "joy"
|
||||
.byte JOY_API_VERSION ; Driver API version number
|
||||
|
||||
; Library reference
|
||||
|
||||
.addr $0000
|
||||
|
||||
; Jump table.
|
||||
|
||||
.addr INSTALL
|
||||
.addr UNINSTALL
|
||||
.addr COUNT
|
||||
.addr READ
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; Constants
|
||||
|
||||
JOY_COUNT = 2 ; Number of joysticks we support
|
||||
|
||||
.code
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; INSTALL routine. Is called after the driver is loaded into memory. If
|
||||
; possible, check if the hardware is present and determine the amount of
|
||||
; memory available.
|
||||
; Must return an JOY_ERR_xx code in a/x.
|
||||
;
|
||||
|
||||
INSTALL:
|
||||
; Assume 7800 2-button controller, can change
|
||||
; to 2600 1-button later
|
||||
lda #$14
|
||||
sta CTLSWB ; enable 2-button 7800 controller 1: set pin 6 to output
|
||||
ldy #$00
|
||||
sty SWCHB ; enable 2-button 7800 controller 2: pull pin 6 (INPT4) high
|
||||
|
||||
reset:
|
||||
lda #<JOY_ERR_OK
|
||||
ldx #>JOY_ERR_OK
|
||||
; rts ; Run into UNINSTALL instead
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; UNINSTALL routine. Is called before the driver is removed from memory.
|
||||
; Can do cleanup or whatever. Must not return anything.
|
||||
;
|
||||
|
||||
UNINSTALL:
|
||||
rts
|
||||
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; COUNT: Return the total number of available joysticks in a/x.
|
||||
;
|
||||
|
||||
COUNT:
|
||||
lda #<JOY_COUNT
|
||||
ldx #>JOY_COUNT
|
||||
rts
|
||||
|
||||
; ------------------------------------------------------------------------
|
||||
; READ: Read a particular joystick passed in A for 2 fire buttons.
|
||||
|
||||
readbuttons:
|
||||
; Y has joystick of interest 0/1
|
||||
; return value:
|
||||
; $00: no button,
|
||||
; $01: left/B button,
|
||||
; $02: right/A button,
|
||||
; $03: both buttons
|
||||
; preserves X
|
||||
tya
|
||||
beq L5
|
||||
; Joystick 1 processing
|
||||
; 7800 joystick 1 buttons
|
||||
ldy #0 ; ........
|
||||
bit INPT2 ; Check for right button
|
||||
bpl L1
|
||||
ldy #2 ; ......2.
|
||||
L1: bit INPT3 ;Check for left button
|
||||
bpl L2
|
||||
iny ; ......21
|
||||
L2: tya
|
||||
bne L4 ; 7800 mode joystick worked
|
||||
; 2600 Joystick 1
|
||||
bit INPT5
|
||||
bmi L4
|
||||
L3: iny ; .......1
|
||||
lda #0 ; Fallback to 2600 joystick mode
|
||||
sta CTLSWB
|
||||
L4: tya ; ......21
|
||||
rts
|
||||
|
||||
L5: ; Joystick 0 processing
|
||||
; 7800 joystick 0 buttons
|
||||
ldy #0 ; ........
|
||||
bit INPT0 ; Check for right button
|
||||
bpl L6
|
||||
ldy #2 ; ......2.
|
||||
L6: bit INPT1 ;Check for left button
|
||||
bpl L7
|
||||
iny ; ......21
|
||||
L7: tya
|
||||
bne L4 ; 7800 mode joystick worked
|
||||
; 2600 Joystick 0
|
||||
bit INPT4
|
||||
bmi L4
|
||||
bpl L3
|
||||
|
||||
READ:
|
||||
tay ; Store joystick 0/1 in Y
|
||||
beq L8
|
||||
lda SWCHA ; Read directions of joystick 1
|
||||
rol ; ...RLDU.
|
||||
rol ; ..RLDU..
|
||||
rol ; .RLDU... - joystick 1
|
||||
jmp L9
|
||||
L8: lda SWCHA ; Read directions of joystick 0
|
||||
ror ; .RLDU... - joystick 0
|
||||
L9: tax
|
||||
jsr readbuttons ; A = ......21, X = .RLDU...
|
||||
ror ; A = .......2 1
|
||||
tay ; Y = .......2
|
||||
txa ; A = .RLDU...
|
||||
ror ; A = 1.RLDU..
|
||||
tax ; X = 1.RLDU..
|
||||
tya ; A = .......2
|
||||
ror ; A = ........ 2
|
||||
txa ; A = 1.RLDU..
|
||||
rol ; A = .RLDU..2 1
|
||||
rol ; A = RLDU..21
|
||||
eor #$F0 ; The direction buttons were inversed
|
||||
and #$F3
|
||||
rts
|
||||
|
14
libsrc/atari7800/joy_stat_stddrv.s
Normal file
14
libsrc/atari7800/joy_stat_stddrv.s
Normal file
@ -0,0 +1,14 @@
|
||||
;
|
||||
; Address of the static standard joystick driver
|
||||
;
|
||||
; Oliver Schmidt, 2012-11-01
|
||||
;
|
||||
; const void joy_static_stddrv[];
|
||||
;
|
||||
|
||||
.export _joy_static_stddrv
|
||||
.import _atari7800_stdjoy_joy
|
||||
|
||||
.rodata
|
||||
|
||||
_joy_static_stddrv := _atari7800_stdjoy_joy
|
8
libsrc/atari7800/libref.s
Normal file
8
libsrc/atari7800/libref.s
Normal file
@ -0,0 +1,8 @@
|
||||
;
|
||||
; Oliver Schmidt, 2013-05-31
|
||||
;
|
||||
|
||||
.export joy_libref
|
||||
.import _exit
|
||||
|
||||
joy_libref := _exit
|
@ -72,7 +72,7 @@ reu_params: .word $0000 ; Host address, lo, hi
|
||||
.byte $00 ; Expansion bank no.
|
||||
.word $0000 ; # bytes to move, lo, hi
|
||||
.byte $00 ; Interrupt mask reg.
|
||||
.byte $00 ; Adress control reg.
|
||||
.byte $00 ; Address control reg.
|
||||
|
||||
.code
|
||||
|
||||
|
@ -73,7 +73,7 @@ reu_params: .word $0000 ; Host address, lo, hi
|
||||
.byte $00 ; Expansion bank no.
|
||||
.word $0000 ; # bytes to move, lo, hi
|
||||
.byte $00 ; Interrupt mask reg.
|
||||
.byte $00 ; Adress control reg.
|
||||
.byte $00 ; Address control reg.
|
||||
|
||||
.code
|
||||
|
||||
|
@ -130,7 +130,7 @@ dowrite:
|
||||
beq notrunc
|
||||
jsr scratch
|
||||
|
||||
; Complete the the file name. Check for append mode here.
|
||||
; Complete the file name. Check for append mode here.
|
||||
|
||||
notrunc:
|
||||
lda tmp3 ; Get the mode again
|
||||
@ -168,7 +168,7 @@ nofile: ; ... else use SA=0 (read)
|
||||
jsr OPEN
|
||||
bcs oserror
|
||||
|
||||
; Open the the drive command channel and read it
|
||||
; Open the drive command channel and read it
|
||||
|
||||
ldx fnunit
|
||||
jsr opencmdchannel
|
||||
|
@ -31,7 +31,7 @@ FILE* __fastcall__ freopen (const char* name, const char* mode, FILE* f)
|
||||
** overwritten by _fopen.
|
||||
*/
|
||||
if (close (f->f_fd) < 0) {
|
||||
/* An error occured, errno is already set */
|
||||
/* An error occurred, errno is already set */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ specval:
|
||||
; Common subroutine to pop the parameters and put them into core
|
||||
;
|
||||
|
||||
dopop: sta tmp1 ; will loose high byte
|
||||
dopop: sta tmp1 ; will lose high byte
|
||||
ldy #0
|
||||
lda (sp),y
|
||||
sta ptr2
|
||||
|
@ -18,7 +18,7 @@
|
||||
; Common subroutine to pop the parameters and put them into core
|
||||
;
|
||||
|
||||
dopop: sta tmp1 ; will loose high byte
|
||||
dopop: sta tmp1 ; will lose high byte
|
||||
jsr popax ; get s to ptr2
|
||||
sta ptr2
|
||||
stx ptr2+1
|
||||
|
@ -0,0 +1 @@
|
||||
; empty file to prevent cbm/cpeeks.s being pulled into the cx16 lib
|
@ -58,7 +58,7 @@ DbgBreak:
|
||||
.res 256
|
||||
DbgStack:
|
||||
|
||||
; Swap space for the the C temporaries
|
||||
; Swap space for the C temporaries
|
||||
|
||||
CTemp:
|
||||
_DbgCS: .res 2 ; sp
|
||||
|
@ -89,16 +89,17 @@ putchar:
|
||||
adc #>(fontdata-$f8)
|
||||
sta ptr3+1
|
||||
|
||||
lda CHARCOLOR
|
||||
and #1
|
||||
beq @skip_plane1
|
||||
|
||||
lda #LCD_XPOS_PLANE1
|
||||
clc
|
||||
adc CURS_X
|
||||
sta LCD_X
|
||||
|
||||
ldy #$f8
|
||||
ldy #$F8
|
||||
|
||||
lda CHARCOLOR
|
||||
lsr
|
||||
bcc @delete1
|
||||
|
||||
@copylp1:
|
||||
lda (ptr3),y
|
||||
eor RVS
|
||||
@ -106,11 +107,16 @@ putchar:
|
||||
iny
|
||||
bne @copylp1
|
||||
|
||||
@skip_plane1:
|
||||
beq @skip_delete1
|
||||
|
||||
lda CHARCOLOR
|
||||
and #2
|
||||
beq @skip_plane2
|
||||
@delete1:
|
||||
lda #$00
|
||||
@del1:
|
||||
sta LCD_DATA
|
||||
iny
|
||||
bne @del1
|
||||
|
||||
@skip_delete1:
|
||||
|
||||
lda #LCD_XPOS_PLANE2
|
||||
clc
|
||||
@ -121,7 +127,12 @@ putchar:
|
||||
lda _plotlo,x
|
||||
sta LCD_Y
|
||||
|
||||
ldy #$f8
|
||||
ldy #$F8
|
||||
|
||||
lda CHARCOLOR
|
||||
and #2
|
||||
beq @delete2
|
||||
|
||||
@copylp2:
|
||||
lda (ptr3),y
|
||||
eor RVS
|
||||
@ -129,7 +140,16 @@ putchar:
|
||||
iny
|
||||
bne @copylp2
|
||||
|
||||
@skip_plane2:
|
||||
beq @skip_delete2
|
||||
|
||||
@delete2:
|
||||
lda #$00
|
||||
@del2:
|
||||
sta LCD_DATA
|
||||
iny
|
||||
bne @del2
|
||||
|
||||
@skip_delete2:
|
||||
pla
|
||||
tax
|
||||
ldy CURS_X
|
||||
|
@ -17,4 +17,3 @@ _CRC:
|
||||
lda r2L
|
||||
ldx r2H
|
||||
rts
|
||||
|
@ -18,44 +18,60 @@
|
||||
; The idea is to make the smalles possible encrypted loader as decryption
|
||||
; is very slow. The minimum size is 49 bytes plus a zero byte.
|
||||
;**********************************
|
||||
; EXE = $fb68
|
||||
; EXE = $fb68
|
||||
;
|
||||
; .org $0200
|
||||
; .org $0200
|
||||
;
|
||||
; ; 1. force Mikey to be in memory
|
||||
; stz MAPCTL
|
||||
; ; 1. force Mikey to be in memory
|
||||
; 9C F9 FF stz MAPCTL
|
||||
;
|
||||
; ; 3. set ComLynx to open collector
|
||||
; lda #4 ; a = 00000100
|
||||
; sta SERCTL ; set the ComLynx to open collector
|
||||
; ; 2. clear palette
|
||||
; A0 1F ldy #31
|
||||
; A9 00 lda #0
|
||||
; 99 A0 FD nextc: sta GCOLMAP, y
|
||||
; 88 dey
|
||||
; 10 FA bpl nextc
|
||||
;
|
||||
; ; 4. make sure the ROM is powered on
|
||||
; lda #8 ; a = 00001000
|
||||
; sta IODAT ; set the ROM power to on
|
||||
; ; 3. set ComLynx to open collector
|
||||
; A9 04 lda #4 ; a = 00000100
|
||||
; 8D 8C FD sta SERCTL ; set the ComLynx to open collector
|
||||
;
|
||||
; ; 5. read in secondary exe + 8 bytes from the cart and store it in $f000
|
||||
; ldx #0 ; x = 0
|
||||
; ldy #$97 ; y = secondary loader size (151 bytes)
|
||||
;rloop1: lda RCART0 ; read a byte from the cart
|
||||
; sta EXE,X ; EXE[X] = a
|
||||
; inx ; x++
|
||||
; dey ; y--
|
||||
; bne rloop1 ; loops until y wraps
|
||||
; ; 4. set AUDIN to output
|
||||
; A9 1A lda #$1a ; audin = out, rest = out,
|
||||
; ; noexp = in, cart addr = out, ext pwd = in
|
||||
; 8D 8A FD sta IODIR
|
||||
;
|
||||
; ; 6. jump to secondary loader
|
||||
; jmp EXE ; run the secondary loader
|
||||
; ; 5. set AUDIN to LOW
|
||||
; A9 0B lda #$0B ; Set AUDIN low
|
||||
; 85 1A sta $1a ; Save local copy to ZP
|
||||
; 8D 8B FD sta IODAT
|
||||
;
|
||||
; .reloc
|
||||
; ; 6. read in secondary exe + 8 bytes from the cart
|
||||
; ; and store it in $f000
|
||||
; A2 00 ldx #0 ; x = 0
|
||||
; A0 97 ldy #$97 ; y = secondary loader size (151 bytes)
|
||||
; AD B2 FC rloop1: lda RCART0 ; read a byte from the cart
|
||||
; 9D 68 FB sta EXE,X ; EXE[X] = a
|
||||
; E8 inx ; x++
|
||||
; 88 dey ; y--
|
||||
; D0 F6 bne rloop1 ; loops until y wraps
|
||||
;
|
||||
; ; 7. jump to secondary loader
|
||||
; 4C 68 FB jmp EXE
|
||||
; 00 00 00 00 ; spares
|
||||
; 00 ; End of encrypted header mark
|
||||
;
|
||||
; .reloc
|
||||
;**********************************
|
||||
; After compilation, encryption and obfuscation it turns into this.
|
||||
;**********************************
|
||||
.byte $ff, $81, $ca, $33, $be, $80, $a2, $c4
|
||||
.byte $6d, $98, $fe, $8d, $bc, $66, $c0, $7a
|
||||
.byte $09, $50, $23, $28, $18, $c8, $06, $70
|
||||
.byte $58, $4f, $1b, $e1, $c7, $90, $08, $cd
|
||||
.byte $1a, $6e, $5a, $45, $32, $d7, $6d, $c6
|
||||
.byte $8a, $e5, $d8, $5c, $a0, $e8, $4f, $7a
|
||||
.byte $5f, $73, $8d, $22
|
||||
.byte $ff, $b6, $bb, $82, $d5, $9f, $48, $cf
|
||||
.byte $23, $37, $8e, $07, $38, $f5, $b6, $30
|
||||
.byte $d6, $2f, $12, $29, $9f, $43, $5b, $2e
|
||||
.byte $f5, $66, $5c, $db, $93, $1a, $78, $55
|
||||
.byte $5e, $c9, $0d, $72, $1b, $e9, $d8, $4d
|
||||
.byte $2f, $e4, $95, $c0, $4f, $7f, $1b, $66
|
||||
.byte $8b, $a7, $fc, $21
|
||||
|
||||
;**********************************
|
||||
; Now we have the secondary loader
|
||||
|
@ -113,7 +113,8 @@ text_bitmap: .res 8*(1+20+1)+1
|
||||
|
||||
.rodata
|
||||
|
||||
DEFPALETTE: .byte >$011
|
||||
DEFPALETTE: .byte >$223
|
||||
.byte >$011
|
||||
.byte >$34d
|
||||
.byte >$9af
|
||||
.byte >$9b8
|
||||
@ -124,11 +125,11 @@ DEFPALETTE: .byte >$011
|
||||
.byte >$d5f
|
||||
.byte >$c53
|
||||
.byte >$822
|
||||
.byte >$223
|
||||
.byte >$484
|
||||
.byte >$8e5
|
||||
.byte >$cf5
|
||||
.byte >$fff
|
||||
.byte <$223
|
||||
.byte <$011
|
||||
.byte <$34d
|
||||
.byte <$9af
|
||||
@ -140,7 +141,6 @@ DEFPALETTE: .byte >$011
|
||||
.byte <$d5f
|
||||
.byte <$c53
|
||||
.byte <$822
|
||||
.byte <$223
|
||||
.byte <$484
|
||||
.byte <$8e5
|
||||
.byte <$cf5
|
||||
@ -162,6 +162,7 @@ INSTALL:
|
||||
lda #1
|
||||
sta TEXTMAGX
|
||||
sta TEXTMAGY
|
||||
sta DRAWINDEX
|
||||
stz BGINDEX
|
||||
stz DRAWPAGE
|
||||
stz SWAPREQUEST
|
||||
@ -418,7 +419,7 @@ cls_sprite:
|
||||
.word 0
|
||||
.word $a000 ; 160
|
||||
.word $6600 ; 102
|
||||
.byte $00
|
||||
.byte $11
|
||||
|
||||
.code
|
||||
CLEAR: lda #<cls_sprite
|
||||
@ -844,11 +845,6 @@ OUTTEXT:
|
||||
lda TEXTMAGY
|
||||
sta text_sy+1
|
||||
|
||||
lda BGINDEX
|
||||
beq @L1 ; Choose opaque black sprite?
|
||||
lda #$04 ; No, choose normal sprite
|
||||
@L1:
|
||||
sta text_sprite
|
||||
lda DRAWINDEX ; Set color
|
||||
asl
|
||||
asl
|
||||
@ -956,7 +952,7 @@ OUTTEXT:
|
||||
text_coll:
|
||||
.byte 0
|
||||
text_sprite:
|
||||
.byte $00,$90,$20
|
||||
.byte $04,$90,$20
|
||||
.addr 0, text_bitmap
|
||||
text_x:
|
||||
.word 0
|
||||
|
@ -4,5 +4,5 @@
|
||||
|
||||
.include "tgi-kernel.inc"
|
||||
|
||||
.export tgi_color_black:zp = $00
|
||||
.export tgi_color_black:zp = $01
|
||||
.export tgi_color_white:zp = $0F
|
||||
|
@ -173,6 +173,7 @@ ScrTabHi:
|
||||
.byte >(ScrBase + ScrFirstChar + I * ScrollDist)
|
||||
.endrep
|
||||
|
||||
|
||||
.code
|
||||
|
||||
osi_cputfuncs ScrBase, ScrFirstChar, ScrWidth, ScrHeight, \
|
||||
|
@ -24,7 +24,7 @@ gotoxy: jsr popa ; Get Y
|
||||
jsr popa
|
||||
sta SCRX
|
||||
|
||||
; Update adress video ram position when SCRY is modified (update_adscr)
|
||||
; Update address video ram position when SCRY is modified (update_adscr)
|
||||
; Fall through
|
||||
.endproc
|
||||
|
||||
|
@ -379,7 +379,7 @@ out:
|
||||
tya
|
||||
tax
|
||||
|
||||
lda ptr3 ; XSCHAR needs in A and Y the adress of the string
|
||||
lda ptr3 ; XSCHAR needs in A and Y the address of the string
|
||||
ldy ptr3+1
|
||||
BRK_TELEMON(XSCHAR)
|
||||
rts
|
||||
|
@ -372,7 +372,7 @@ out:
|
||||
tya
|
||||
tax
|
||||
|
||||
lda ptr3 ; XSCHAR needs in A and Y the adress of the string
|
||||
lda ptr3 ; XSCHAR needs in A and Y the address of the string
|
||||
ldy ptr3+1
|
||||
BRK_TELEMON(XSCHAR)
|
||||
rts
|
||||
|
@ -159,7 +159,7 @@ tgi_clip_sign: .res 1
|
||||
|
||||
|
||||
;----------------------------------------------------------------------------
|
||||
; Multiplicate value in y/a by dy, then divide by dx.
|
||||
; Multiply value in y/a by dy, then divide by dx.
|
||||
;
|
||||
|
||||
.code
|
||||
@ -176,7 +176,7 @@ tgi_clip_sign: .res 1
|
||||
|
||||
lda tgi_clip_dy
|
||||
ldx tgi_clip_dy+1 ; rhs
|
||||
jsr umul16x16r32 ; Multiplicate
|
||||
jsr umul16x16r32 ; Multiply
|
||||
|
||||
; Move the result of the multiplication into ptr1:ptr2
|
||||
|
||||
@ -199,7 +199,7 @@ done: bit tmp1
|
||||
|
||||
|
||||
;----------------------------------------------------------------------------
|
||||
; Multiplicate value in y/a by dx, then divide by dy.
|
||||
; Multiply value in y/a by dx, then divide by dy.
|
||||
;
|
||||
|
||||
.code
|
||||
@ -216,7 +216,7 @@ done: bit tmp1
|
||||
|
||||
lda tgi_clip_dx
|
||||
ldx tgi_clip_dx+1 ; rhs
|
||||
jsr umul16x16r32 ; Multiplicate
|
||||
jsr umul16x16r32 ; Multiply
|
||||
|
||||
; Move the result of the multiplication into ptr1:ptr2
|
||||
|
||||
|
@ -33,7 +33,7 @@ _tgi_imulround:
|
||||
; ASM callable entry point
|
||||
tgi_imulround:
|
||||
|
||||
; Multiplicate
|
||||
; Multiply
|
||||
|
||||
jsr imul16x16r32
|
||||
|
||||
|
@ -79,9 +79,9 @@ GetProcessedCoord:
|
||||
|
||||
jsr GetOp
|
||||
|
||||
; Multiplicate with the scale factor.
|
||||
; Multiply with the scale factor.
|
||||
|
||||
jmp tgi_imulround ; Multiplicate, round and scale
|
||||
jmp tgi_imulround ; Multiply, round and scale
|
||||
|
||||
;----------------------------------------------------------------------------
|
||||
; Add the base coordinate with offset in Y to the value in A/X
|
||||
@ -133,7 +133,7 @@ GetProcessedCoord:
|
||||
.code
|
||||
.proc _tgi_vectorchar
|
||||
|
||||
; Multiplicate the char value by two and save into Y
|
||||
; Multiply the char value by two and save into Y
|
||||
|
||||
asl a
|
||||
tay
|
||||
|
@ -154,7 +154,7 @@ endif
|
||||
# Lists of subdirectories
|
||||
|
||||
# disasm depends on cpp
|
||||
DIRLIST = tutorial geos atari2600 atari5200 apple2 gamate supervision sym1 cbm
|
||||
DIRLIST = tutorial geos atari2600 atari5200 apple2 gamate lynx supervision sym1 cbm
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Lists of executables
|
||||
@ -199,7 +199,8 @@ EXELIST_atmos = \
|
||||
ascii \
|
||||
hello \
|
||||
mandelbrot \
|
||||
sieve
|
||||
sieve \
|
||||
tgidemo
|
||||
|
||||
EXELIST_bbc = \
|
||||
notavailable
|
||||
@ -321,7 +322,7 @@ EXELIST_telestrat = \
|
||||
hello \
|
||||
mandelbrot \
|
||||
sieve \
|
||||
# tgidemo
|
||||
tgidemo
|
||||
|
||||
EXELIST_vic20 = \
|
||||
ascii \
|
||||
|
@ -53,7 +53,7 @@ test.s: test.grc
|
||||
$(GRC) -s test.s test.grc
|
||||
|
||||
vlir.cvt: vlir.grc vlir0.s vlir1.s vlir2.s
|
||||
# using seperate calls here for demonstration purposes:
|
||||
# using separate calls here for demonstration purposes:
|
||||
$(GRC) -t $(SYS) -s vlir.s vlir.grc
|
||||
$(AS) -t $(SYS) vlir.s
|
||||
$(AS) -t $(SYS) vlir0.s
|
||||
|
59
samples/lynx/Makefile
Normal file
59
samples/lynx/Makefile
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
# Run 'make SYS=<target>'; or, set a SYS env.
|
||||
# var. to build for another target system.
|
||||
SYS ?= lynx
|
||||
|
||||
# Just the usual way to find out if we're
|
||||
# using cmd.exe to execute make rules.
|
||||
ifneq ($(shell echo),)
|
||||
CMD_EXE = 1
|
||||
endif
|
||||
|
||||
ifdef CMD_EXE
|
||||
NULLDEV = nul:
|
||||
DEL = -del /f
|
||||
else
|
||||
NULLDEV = /dev/null
|
||||
DEL = $(RM)
|
||||
endif
|
||||
|
||||
ifdef CC65_HOME
|
||||
AS = $(CC65_HOME)/bin/ca65
|
||||
CC = $(CC65_HOME)/bin/cc65
|
||||
CL = $(CC65_HOME)/bin/cl65
|
||||
LD = $(CC65_HOME)/bin/ld65
|
||||
else
|
||||
AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65)
|
||||
CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65)
|
||||
CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65)
|
||||
LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65)
|
||||
endif
|
||||
|
||||
EXELIST_lynx = \
|
||||
hello.lnx \
|
||||
mandelbrot.lnx \
|
||||
tgidemo.lnx
|
||||
|
||||
.PHONY: samples clean
|
||||
|
||||
ifneq ($(EXELIST_$(SYS)),)
|
||||
samples: $(EXELIST_$(SYS))
|
||||
else
|
||||
samples:
|
||||
# recipe used to skip systems that will not work with any program in this dir
|
||||
ifeq ($(MAKELEVEL),0)
|
||||
@echo "info: Lynx tests not available for" $(SYS)
|
||||
else
|
||||
# Suppress the "nothing to be done for 'samples' message.
|
||||
@echo "" > $(NULLDEV)
|
||||
endif
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .lnx
|
||||
|
||||
%.lnx : %.c
|
||||
$(CL) -t $(SYS) -Oris -m $*.map -o $@ $<
|
||||
|
||||
clean:
|
||||
@$(DEL) *.o *.map *.lnx 2>$(NULLDEV)
|
43
samples/lynx/hello.c
Normal file
43
samples/lynx/hello.c
Normal file
@ -0,0 +1,43 @@
|
||||
/* Atari Lynx version of samples/hello.c, using TGI instead of conio */
|
||||
|
||||
#include <tgi.h>
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
static const char Text[] = "Hello world!";
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
void main (void)
|
||||
{
|
||||
unsigned int XMax, YMax;
|
||||
|
||||
tgi_install (tgi_static_stddrv);
|
||||
tgi_init ();
|
||||
|
||||
/* Set screen color. */
|
||||
tgi_setcolor (TGI_COLOR_WHITE);
|
||||
|
||||
/* Clear the screen. */
|
||||
tgi_clear();
|
||||
|
||||
/* Ask for the screen size. */
|
||||
XMax = tgi_getmaxx ();
|
||||
YMax = tgi_getmaxy ();
|
||||
|
||||
/* Draw a frame around the screen. */
|
||||
tgi_line (0, 0, XMax, 0);
|
||||
tgi_lineto (XMax, YMax);
|
||||
tgi_lineto (0, YMax);
|
||||
tgi_lineto (0, 0);
|
||||
|
||||
/* Write the greeting in the middle of the screen. */
|
||||
tgi_outtextxy ((tgi_getxres () - tgi_gettextwidth (Text)) / 2,
|
||||
(tgi_getyres () - tgi_gettextheight (Text)) / 2, Text);
|
||||
}
|
86
samples/lynx/mandelbrot.c
Normal file
86
samples/lynx/mandelbrot.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*****************************************************************************\
|
||||
** mandelbrot sample program for Atari Lynx **
|
||||
** **
|
||||
** (w) 2002 by groepaz/hitmen, TGI support by Stefan Haubenthal **
|
||||
\*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <tgi.h>
|
||||
|
||||
|
||||
|
||||
/* Graphics definitions */
|
||||
#define SCREEN_X (tgi_getxres())
|
||||
#define SCREEN_Y (tgi_getyres())
|
||||
#define MAXCOL (tgi_getcolorcount())
|
||||
|
||||
#define maxiterations 32
|
||||
#define fpshift (10)
|
||||
#define tofp(_x) ((_x)<<fpshift)
|
||||
#define fromfp(_x) ((_x)>>fpshift)
|
||||
#define fpabs(_x) (abs(_x))
|
||||
|
||||
#define mulfp(_a,_b) ((((signed long)_a)*(_b))>>fpshift)
|
||||
#define divfp(_a,_b) ((((signed long)_a)<<fpshift)/(_b))
|
||||
|
||||
/* Use static local variables for speed */
|
||||
#pragma static-locals (1);
|
||||
|
||||
|
||||
|
||||
static void mandelbrot (signed short x1, signed short y1, signed short x2,
|
||||
signed short y2)
|
||||
{
|
||||
/* */
|
||||
register signed short r, r1, i;
|
||||
register signed short xs, ys, xx, yy;
|
||||
register signed short x, y;
|
||||
register unsigned char count;
|
||||
register unsigned char maxcol = MAXCOL;
|
||||
|
||||
/* Calc stepwidth */
|
||||
xs = ((x2 - x1) / (SCREEN_X));
|
||||
ys = ((y2 - y1) / (SCREEN_Y));
|
||||
|
||||
yy = y1;
|
||||
for (y = 0; y < (SCREEN_Y); y++) {
|
||||
yy += ys;
|
||||
xx = x1;
|
||||
for (x = 0; x < (SCREEN_X); x++) {
|
||||
xx += xs;
|
||||
/* Do iterations */
|
||||
r = 0;
|
||||
i = 0;
|
||||
for (count = 0; (count < maxiterations) &&
|
||||
(fpabs (r) < tofp (2)) && (fpabs (i) < tofp (2));
|
||||
++count) {
|
||||
r1 = (mulfp (r, r) - mulfp (i, i)) + xx;
|
||||
/* i = (mulfp(mulfp(r,i),tofp(2)))+yy; */
|
||||
i = (((signed long) r * i) >> (fpshift - 1)) + yy;
|
||||
r = r1;
|
||||
}
|
||||
if (count == maxiterations) {
|
||||
tgi_setcolor (0);
|
||||
} else {
|
||||
tgi_setcolor (count % maxcol);
|
||||
}
|
||||
/* Set pixel */
|
||||
tgi_setpixel (x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main (void)
|
||||
{
|
||||
/* Install the graphics driver */
|
||||
tgi_install (tgi_static_stddrv);
|
||||
|
||||
/* Initialize graphics */
|
||||
tgi_init ();
|
||||
tgi_clear ();
|
||||
|
||||
/* Calc mandelbrot set */
|
||||
mandelbrot (tofp (-2), tofp (-2), tofp (2), tofp (2));
|
||||
}
|
179
samples/lynx/tgidemo.c
Normal file
179
samples/lynx/tgidemo.c
Normal file
@ -0,0 +1,179 @@
|
||||
/* Tgidemo modified for the Atari Lynx.
|
||||
**
|
||||
** Press any of the Lynx's option buttons to go to the next screen.
|
||||
*/
|
||||
|
||||
#include <cc65.h>
|
||||
#include <conio.h>
|
||||
#include <tgi.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#define COLOR_BACK TGI_COLOR_BLACK
|
||||
#define COLOR_FORE TGI_COLOR_WHITE
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Driver stuff */
|
||||
static unsigned MaxX;
|
||||
static unsigned MaxY;
|
||||
static unsigned AspectRatio;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* The Lynx draws too fast. This function delays
|
||||
** the drawing so that we can watch it.
|
||||
*/
|
||||
static void wait (unsigned char ticks)
|
||||
{
|
||||
clock_t T = clock () + ticks;
|
||||
|
||||
while (clock () < T) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void DoCircles (void)
|
||||
{
|
||||
unsigned char I;
|
||||
unsigned char Color = COLOR_BACK;
|
||||
const unsigned X = MaxX / 2;
|
||||
const unsigned Y = MaxY / 2;
|
||||
const unsigned Limit = (X < Y) ? Y : X;
|
||||
|
||||
tgi_setcolor (COLOR_FORE);
|
||||
tgi_clear ();
|
||||
|
||||
tgi_line (0, 0, MaxX, MaxY);
|
||||
tgi_line (0, MaxY, MaxX, 0);
|
||||
while (!kbhit ()) {
|
||||
Color = (Color == COLOR_FORE) ? COLOR_BACK : COLOR_FORE;
|
||||
tgi_setcolor (Color);
|
||||
for (I = 10; I <= Limit; I += 10) {
|
||||
tgi_ellipse (X, Y, I, tgi_imulround (I, AspectRatio));
|
||||
wait (9);
|
||||
}
|
||||
}
|
||||
|
||||
cgetc ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void DoCheckerboard (void)
|
||||
{
|
||||
unsigned X, Y;
|
||||
unsigned char Color = COLOR_BACK;
|
||||
|
||||
tgi_clear ();
|
||||
|
||||
while (1) {
|
||||
for (Y = 0; Y <= MaxY - 2; Y += 10) {
|
||||
for (X = 0; X <= MaxX; X += 10) {
|
||||
Color = (Color == COLOR_FORE) ? COLOR_BACK : COLOR_FORE;
|
||||
tgi_setcolor (Color);
|
||||
tgi_bar (X, Y, X+9, Y+9);
|
||||
if (kbhit ()) {
|
||||
cgetc ();
|
||||
return;
|
||||
}
|
||||
wait (1);
|
||||
}
|
||||
Color = Color == COLOR_FORE ? COLOR_BACK : COLOR_FORE;
|
||||
}
|
||||
Color = Color == COLOR_FORE ? COLOR_BACK : COLOR_FORE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void DoDiagram (void)
|
||||
{
|
||||
int XOrigin, YOrigin;
|
||||
int Amp;
|
||||
int X, Y;
|
||||
unsigned I;
|
||||
|
||||
tgi_setcolor (COLOR_FORE);
|
||||
tgi_clear ();
|
||||
|
||||
/* Determine zero and amplitude */
|
||||
YOrigin = MaxY / 2;
|
||||
XOrigin = 10;
|
||||
Amp = (MaxY - 19) / 2;
|
||||
|
||||
/* Y axis */
|
||||
tgi_line (XOrigin, 10, XOrigin, MaxY-10);
|
||||
tgi_line (XOrigin-2, 12, XOrigin, 10);
|
||||
tgi_lineto (XOrigin+2, 12);
|
||||
|
||||
/* X axis */
|
||||
tgi_line (XOrigin, YOrigin, MaxX-10, YOrigin);
|
||||
tgi_line (MaxX-12, YOrigin-2, MaxX-10, YOrigin);
|
||||
tgi_lineto (MaxX-12, YOrigin+2);
|
||||
|
||||
/* Sine */
|
||||
tgi_gotoxy (XOrigin, YOrigin);
|
||||
for (I = 0; I <= 360; ++I) {
|
||||
/* Calculate the next points */
|
||||
X = (int)(((long)(MaxX - 19) * I) / 360);
|
||||
Y = (int)(((long)Amp * -_sin (I)) / 256);
|
||||
|
||||
/* Draw the line */
|
||||
tgi_lineto (XOrigin + X, YOrigin + Y);
|
||||
}
|
||||
|
||||
cgetc ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void DoLines (void)
|
||||
{
|
||||
unsigned X;
|
||||
const unsigned Min = (MaxX < MaxY) ? MaxX : MaxY;
|
||||
|
||||
tgi_setcolor (COLOR_FORE);
|
||||
tgi_clear ();
|
||||
|
||||
for (X = 0; X <= Min; X += 10) {
|
||||
tgi_line (0, 0, Min, X);
|
||||
tgi_line (0, 0, X, Min);
|
||||
tgi_line (Min, Min, 0, Min-X);
|
||||
tgi_line (Min, Min, Min-X, 0);
|
||||
wait (9);
|
||||
}
|
||||
|
||||
cgetc ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void main (void)
|
||||
{
|
||||
/* Install the driver */
|
||||
tgi_install (tgi_static_stddrv);
|
||||
tgi_init ();
|
||||
|
||||
/* Get stuff from the driver */
|
||||
MaxX = tgi_getmaxx ();
|
||||
MaxY = tgi_getmaxy ();
|
||||
AspectRatio = tgi_getaspectratio ();
|
||||
|
||||
/* Do graphics stuff */
|
||||
DoCircles ();
|
||||
DoCheckerboard ();
|
||||
DoDiagram ();
|
||||
DoLines ();
|
||||
}
|
@ -237,7 +237,7 @@ void main (void)
|
||||
|
||||
/* The linker makes sure that the call to foo() ends up at the right mem
|
||||
** addr. However it's up to user to make sure that the - right - overlay
|
||||
** is actually loaded before making the the call.
|
||||
** is actually loaded before making the call.
|
||||
*/
|
||||
foo ();
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ void main (void)
|
||||
|
||||
/* The linker makes sure that the call to foo() ends up at the right mem
|
||||
** addr. However it's up to user to make sure that the - right - overlay
|
||||
** is actually loaded before making the the call.
|
||||
** is actually loaded before making the call.
|
||||
*/
|
||||
foo ();
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ Platforms: Runs on all platforms that have TGI support:
|
||||
|
||||
=============================================================================
|
||||
|
||||
Platform specific samples follow:
|
||||
Platform-specific samples follow:
|
||||
|
||||
atari 2600:
|
||||
-----------
|
||||
@ -198,6 +198,27 @@ Name: nachtm
|
||||
Description: Plays "Eine kleine Nachtmusik" by Wolfgang Amadeus Mozart.
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
lynx:
|
||||
-----
|
||||
|
||||
These programs are adapted for the Atari Lynx because its library has no conio
|
||||
output or stdio.
|
||||
|
||||
Name: hello
|
||||
Description: A nice "Hello world" type program that uses the TGI graphics
|
||||
library for output.
|
||||
|
||||
Name: mandelbrot
|
||||
Description: A mandelbrot demo using integer arithmetic. The demo was
|
||||
written by groepaz, and converted to cc65 using TGI graphics
|
||||
by Stefan Haubenthal.
|
||||
|
||||
Name: tgidemo
|
||||
Description: Shows some of the graphics capabilities of the "Tiny Graphics
|
||||
Interface".
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
sym1:
|
||||
-----
|
||||
|
||||
|
@ -32,7 +32,7 @@ else
|
||||
endif
|
||||
|
||||
EXELIST_sym1 = \
|
||||
symHello.bin symTiny.bin symDisplay.bin symIO.bin symNotepad.bin
|
||||
symHello.bin symTiny.bin symDisplay.bin symIO.bin symNotepad.bin symExtendedMemory.bin
|
||||
|
||||
ifneq ($(EXELIST_$(SYS)),)
|
||||
samples: $(EXELIST_$(SYS))
|
||||
@ -64,9 +64,14 @@ symIO.bin: symIO.c
|
||||
symNotepad.bin: symNotepad.c
|
||||
$(CL) -t sym1 -C sym1-32k.cfg -O -o symNotepad.bin symNotepad.c
|
||||
|
||||
symExtendedMemory.bin: symExtendedMemory.c
|
||||
$(CL) -t sym1 -C sym1-32k.cfg -O -o symExtendedMemory.bin symExtendedMemory.c
|
||||
|
||||
|
||||
clean:
|
||||
@$(DEL) symHello.bin 2>$(NULLDEV)
|
||||
@$(DEL) symTiny.bin 2>$(NULLDEV)
|
||||
@$(DEL) symDisplay.bin 2>$(NULLDEV)
|
||||
@$(DEL) symIO.bin 2>$(NULLDEV)
|
||||
@$(DEL) symNotepad.bin 2>$(NULLDEV)
|
||||
@$(DEL) symExtendedMemory.bin 2>$(NULLDEV)
|
||||
|
101
samples/sym1/symExtendedMemory.c
Normal file
101
samples/sym1/symExtendedMemory.c
Normal file
@ -0,0 +1,101 @@
|
||||
// --------------------------------------------------------------------------
|
||||
// Sym-1 Extended Memory
|
||||
//
|
||||
// Wayne Parham
|
||||
//
|
||||
// wayne@parhamdata.com
|
||||
// --------------------------------------------------------------------------
|
||||
//
|
||||
// Note: This program examines memory above the monitor ROM (8000-8FFF) to
|
||||
// Determine what, if any, memory is available. It then adds whatever
|
||||
// 4K segments it finds to the heap.
|
||||
//
|
||||
// Memory Segment Remark
|
||||
// 0x9000 Usually available
|
||||
// 0xA000 System I/O, always unavailable
|
||||
// 0xB000 Used by RAE, but normally available
|
||||
// 0xC000 Used by BASIC, normally unavailable
|
||||
// 0xD000 Used by BASIC, normally unavailable
|
||||
// 0xE000 Used by RAE, but normally available
|
||||
// 0xF000 Normally available, but only to FF7F
|
||||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
#include <sym1.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define STD_MEM 0x7FFF // Last address of standard memory
|
||||
#define SEGMENT 0x9000 // First 4K segment of extended memory
|
||||
#define SEG_END 0x0FFF // Last location of segment
|
||||
#define BLOCK_SIZE 0x1000 // Size of segment
|
||||
#define TOP_END 0x0F7F // Last location of memory
|
||||
#define TOP_SIZE 0x0F80 // Size of top segment
|
||||
#define UNAVAILABLE 0xA000 // System I/O area
|
||||
|
||||
int main (void) {
|
||||
int error = 0;
|
||||
unsigned heap_size = 0x0000;
|
||||
char* segment = (char*) SEGMENT;
|
||||
|
||||
printf ( "Analyzing memory.\n\n" );
|
||||
|
||||
heap_size = _heapmemavail();
|
||||
|
||||
printf ( "Main memory has %u bytes available.\n", heap_size );
|
||||
|
||||
if ( heap_size > STD_MEM ) {
|
||||
printf ( "Extended memory already installed.\n" );
|
||||
} else {
|
||||
|
||||
while ( (int) segment < 0xEFFF ) { // Iterate through 4K memory blocks
|
||||
if( (int) segment != UNAVAILABLE ) {
|
||||
segment[0] = 0x00; // Check beginning of segment
|
||||
if ( segment[0] != 0x00 )
|
||||
error = 1;
|
||||
segment[0] = 0xFF;
|
||||
if ( segment[0] != 0xFF )
|
||||
error = 1;
|
||||
segment[SEG_END] = 0x00; // Check end of segment
|
||||
if ( segment[SEG_END] != 0x00 )
|
||||
error = 1;
|
||||
segment[SEG_END] = 0xFF;
|
||||
if ( segment[SEG_END] != 0xFF )
|
||||
error = 1;
|
||||
if ( ! error ) { // If memory found, add to the heap
|
||||
printf ( "Memory found at location %p, ", segment );
|
||||
_heapadd ( segment, BLOCK_SIZE );
|
||||
heap_size = _heapmemavail();
|
||||
printf( "so the system now has %u bytes available.\n", heap_size );
|
||||
} else {
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
segment += 0x1000; // Increment to next segment
|
||||
}
|
||||
|
||||
segment[0] = 0x00; // Check beginning of top memory segment
|
||||
if ( segment[0] != 0x00 )
|
||||
error = 1;
|
||||
segment[0] = 0xFF;
|
||||
if ( segment[0] != 0xFF )
|
||||
error = 1;
|
||||
segment[TOP_END] = 0x00; // Check end of usable memory
|
||||
if ( segment[TOP_END] != 0x00 )
|
||||
error = 1;
|
||||
segment[TOP_END] = 0xFF;
|
||||
if ( segment[TOP_END] != 0xFF )
|
||||
error = 1;
|
||||
if ( ! error ) { // If memory found, add to the heap
|
||||
printf ( "Memory found at location %p, ", segment );
|
||||
_heapadd ( segment, TOP_SIZE );
|
||||
heap_size = _heapmemavail();
|
||||
printf( "so the system now has %u bytes available.\n", heap_size );
|
||||
}
|
||||
}
|
||||
|
||||
puts ("\nEnjoy your day!\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -138,21 +138,21 @@ int main (void) {
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( l = 0; l <= heap_size; l++ ) {
|
||||
buffer[l] = tapio[l];
|
||||
}
|
||||
for ( l = 0; l <= heap_size; l++ ) {
|
||||
buffer[l] = tapio[l];
|
||||
}
|
||||
|
||||
p = strlen ( buffer );
|
||||
p = strlen ( buffer );
|
||||
|
||||
putchar ( '\r' );
|
||||
for ( l = 0; l < 25; l++ ) {
|
||||
putchar ( '\n' );
|
||||
}
|
||||
puts ("===================== Sym-1 Notepad ====================\n");
|
||||
putchar ( '\r' );
|
||||
for ( l = 0; l < 25; l++ ) {
|
||||
putchar ( '\n' );
|
||||
}
|
||||
puts ("===================== Sym-1 Notepad ====================\n");
|
||||
|
||||
for ( l = 0; l <= p; l++ ) {
|
||||
putchar ( buffer[l] );
|
||||
}
|
||||
for ( l = 0; l <= p; l++ ) {
|
||||
putchar ( buffer[l] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( c == 0x03 ) { // Clear
|
||||
|
@ -68,7 +68,7 @@ static void DoWarning (void)
|
||||
|
||||
static void DoCircles (void)
|
||||
{
|
||||
static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_ORANGE };
|
||||
static const unsigned char Palette[2] = { TGI_COLOR_WHITE, TGI_COLOR_BLUE };
|
||||
unsigned char I;
|
||||
unsigned char Color = COLOR_BACK;
|
||||
const unsigned X = MaxX / 2;
|
||||
|
@ -119,6 +119,7 @@ endif # CMD_EXE
|
||||
|
||||
zip:
|
||||
@cd .. && zip cc65 bin/*
|
||||
@echo 'https://github.com/cc65/cc65/commits/'$(word 2,$(BUILD_ID))|zip -zq ../cc65
|
||||
|
||||
define OBJS_template
|
||||
|
||||
|
@ -1708,7 +1708,7 @@ ExprNode* GenLiteralExpr (long Val)
|
||||
|
||||
|
||||
ExprNode* GenLiteral0 (void)
|
||||
/* Return an expression tree that encodes the the number zero */
|
||||
/* Return an expression tree that encodes the number zero */
|
||||
{
|
||||
return GenLiteralExpr (0);
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ ExprNode* GenLiteralExpr (long Val);
|
||||
/* Return an expression tree that encodes the given literal value */
|
||||
|
||||
ExprNode* GenLiteral0 (void);
|
||||
/* Return an expression tree that encodes the the number zero */
|
||||
/* Return an expression tree that encodes the number zero */
|
||||
|
||||
ExprNode* GenSymExpr (struct SymEntry* Sym);
|
||||
/* Return an expression node that encodes the given symbol */
|
||||
|
@ -429,7 +429,7 @@ void ReleaseFullLineInfo (Collection* LineInfos)
|
||||
|
||||
/* Walk over all entries */
|
||||
for (I = 0; I < CollCount (LineInfos); ++I) {
|
||||
/* Release the the line info */
|
||||
/* Release the line info */
|
||||
ReleaseLineInfo (CollAt (LineInfos, I));
|
||||
}
|
||||
|
||||
|
@ -637,7 +637,7 @@ void MacUndef (const StrBuf* Name, unsigned char Style)
|
||||
|
||||
|
||||
static int MacExpand (void* Data)
|
||||
/* If we're currently expanding a macro, set the the scanner token and
|
||||
/* If we're currently expanding a macro, set the scanner token and
|
||||
** attribute to the next value and return true. If we are not expanding
|
||||
** a macro, return false.
|
||||
*/
|
||||
|
@ -212,6 +212,10 @@ static void SetSys (const char* Sys)
|
||||
NewSymbol ("__ATARI5200__", 1);
|
||||
break;
|
||||
|
||||
case TGT_ATARI7800:
|
||||
NewSymbol ("__ATARI7800__", 1);
|
||||
break;
|
||||
|
||||
case TGT_ATARI:
|
||||
NewSymbol ("__ATARI__", 1);
|
||||
break;
|
||||
|
@ -711,7 +711,7 @@ static void StudyMul (ExprNode* Expr, ExprDesc* D)
|
||||
*/
|
||||
if (ED_IsConst (D) && ED_IsValid (&Right)) {
|
||||
|
||||
/* Multiplicate both, result goes into Right */
|
||||
/* Multiply both, result goes into Right */
|
||||
ED_Mul (&Right, D);
|
||||
|
||||
/* Move result into D */
|
||||
@ -719,7 +719,7 @@ static void StudyMul (ExprNode* Expr, ExprDesc* D)
|
||||
|
||||
} else if (ED_IsConst (&Right) && ED_IsValid (D)) {
|
||||
|
||||
/* Multiplicate both */
|
||||
/* Multiply both */
|
||||
ED_Mul (D, &Right);
|
||||
|
||||
} else {
|
||||
|
@ -93,6 +93,7 @@
|
||||
<ClInclude Include="cc65\hexval.h" />
|
||||
<ClInclude Include="cc65\ident.h" />
|
||||
<ClInclude Include="cc65\incpath.h" />
|
||||
<ClInclude Include="cc65\initdata.h" />
|
||||
<ClInclude Include="cc65\input.h" />
|
||||
<ClInclude Include="cc65\lineinfo.h" />
|
||||
<ClInclude Include="cc65\litpool.h" />
|
||||
@ -170,6 +171,7 @@
|
||||
<ClCompile Include="cc65\hexval.c" />
|
||||
<ClCompile Include="cc65\ident.c" />
|
||||
<ClCompile Include="cc65\incpath.c" />
|
||||
<ClCompile Include="cc65\initdata.c" />
|
||||
<ClCompile Include="cc65\input.c" />
|
||||
<ClCompile Include="cc65\lineinfo.c" />
|
||||
<ClCompile Include="cc65\litpool.c" />
|
||||
|
@ -156,19 +156,8 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult)
|
||||
unsigned ChunkFlags;
|
||||
const Type* ChunkType;
|
||||
|
||||
/* If the bit-field fits within one byte, do the following operations
|
||||
** with bytes.
|
||||
*/
|
||||
if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS ==
|
||||
(Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) {
|
||||
ChunkType = GetUnderlyingType (Expr->Type);
|
||||
} else {
|
||||
/* We use the declarartion integer type as the chunk type.
|
||||
** Note: A bit-field will not occupy bits located in bytes more than
|
||||
** that of its declaration type in cc65. So this is OK.
|
||||
*/
|
||||
ChunkType = Expr->Type + 1;
|
||||
}
|
||||
/* Determine the type to operate on the whole byte chunk containing the bit-field */
|
||||
ChunkType = GetBitFieldChunkType (Expr->Type);
|
||||
|
||||
/* Determine code generator flags */
|
||||
Flags = TypeOf (Expr->Type) | CF_FORCECHAR;
|
||||
@ -254,19 +243,8 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op
|
||||
ED_Init (&Expr2);
|
||||
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
|
||||
|
||||
/* If the bit-field fits within one byte, do the following operations
|
||||
** with bytes.
|
||||
*/
|
||||
if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS ==
|
||||
(Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) {
|
||||
ChunkType = GetUnderlyingType (Expr->Type);
|
||||
} else {
|
||||
/* We use the declarartion integer type as the chunk type.
|
||||
** Note: A bit-field will not occupy bits located in bytes more than
|
||||
** that of its declaration type in cc65. So this is OK.
|
||||
*/
|
||||
ChunkType = Expr->Type + 1;
|
||||
}
|
||||
/* Determine the type to operate on the whole byte chunk containing the bit-field */
|
||||
ChunkType = GetBitFieldChunkType (Expr->Type);
|
||||
|
||||
/* Determine code generator flags */
|
||||
Flags = TypeOf (Expr->Type) | CF_FORCECHAR;
|
||||
@ -620,8 +598,8 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
|
||||
if (IsClassStruct (ltype)) {
|
||||
/* Copy the struct or union by value */
|
||||
CopyStruct (Expr, &Expr2);
|
||||
} else if (IsTypeBitField (ltype)) {
|
||||
/* Special care is needed for bit-field 'op=' */
|
||||
} else if (IsTypeFragBitField (ltype)) {
|
||||
/* Special care is needed for bit-fields if they don't fit in full bytes */
|
||||
OpAssignBitField (Gen, Expr, Op);
|
||||
} else {
|
||||
/* Normal straight 'op=' */
|
||||
|
@ -95,7 +95,7 @@ void FreeCaseNodeColl (Collection* Nodes)
|
||||
int SearchCaseNode (const Collection* Nodes, unsigned char Key, int* Index)
|
||||
/* Search for a node in the given collection. If the node has been found,
|
||||
** set Index to the index of the node and return true. If the node was not
|
||||
** found, set Index the the insertion position of the node and return
|
||||
** found, set Index the insertion position of the node and return
|
||||
** false.
|
||||
*/
|
||||
{
|
||||
|
@ -116,7 +116,7 @@ void FreeCaseNodeColl (Collection* Nodes);
|
||||
int SearchCaseNode (const Collection* Nodes, unsigned char Key, int* Index);
|
||||
/* Search for a node in the given collection. If the node has been found,
|
||||
** set Index to the index of the node and return true. If the node was not
|
||||
** found, set Index the the insertion position of the node and return
|
||||
** found, set Index to the insertion position of the node and return
|
||||
** false.
|
||||
*/
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@ -42,7 +43,7 @@
|
||||
#include "addrsize.h"
|
||||
#include "check.h"
|
||||
#include "cpu.h"
|
||||
#include "inttypes.h"
|
||||
#include "shift.h"
|
||||
#include "strbuf.h"
|
||||
#include "xmalloc.h"
|
||||
#include "xsprintf.h"
|
||||
@ -689,7 +690,7 @@ void g_restore_regvars (int StackOffs, int RegOffs, unsigned Bytes)
|
||||
|
||||
|
||||
|
||||
void g_getimmed (unsigned Flags, unsigned long Val, long Offs)
|
||||
void g_getimmed (unsigned Flags, uintptr_t Val, long Offs)
|
||||
/* Load a constant into the primary register */
|
||||
{
|
||||
unsigned char B1, B2, B3, B4;
|
||||
@ -1409,7 +1410,7 @@ static unsigned g_intpromotion (unsigned flags)
|
||||
unsigned g_typeadjust (unsigned lhs, unsigned rhs)
|
||||
/* Adjust the integer operands before doing a binary operation. lhs is a flags
|
||||
** value, that corresponds to the value on TOS, rhs corresponds to the value
|
||||
** in (e)ax. The return value is the the flags value for the resulting type.
|
||||
** in (e)ax. The return value is the flags value for the resulting type.
|
||||
*/
|
||||
{
|
||||
/* Get the type spec from the flags */
|
||||
@ -4394,7 +4395,7 @@ void g_res (unsigned n)
|
||||
|
||||
|
||||
|
||||
void g_defdata (unsigned flags, unsigned long val, long offs)
|
||||
void g_defdata (unsigned flags, uintptr_t val, long offs)
|
||||
/* Define data with the size given in flags */
|
||||
{
|
||||
if (flags & CF_CONST) {
|
||||
@ -4403,15 +4404,15 @@ void g_defdata (unsigned flags, unsigned long val, long offs)
|
||||
switch (flags & CF_TYPEMASK) {
|
||||
|
||||
case CF_CHAR:
|
||||
AddDataLine ("\t.byte\t$%02lX", val & 0xFF);
|
||||
AddDataLine ("\t.byte\t$%02"PRIXPTR, val & 0xFF);
|
||||
break;
|
||||
|
||||
case CF_INT:
|
||||
AddDataLine ("\t.word\t$%04lX", val & 0xFFFF);
|
||||
AddDataLine ("\t.word\t$%04"PRIXPTR, val & 0xFFFF);
|
||||
break;
|
||||
|
||||
case CF_LONG:
|
||||
AddDataLine ("\t.dword\t$%08lX", val & 0xFFFFFFFF);
|
||||
AddDataLine ("\t.dword\t$%08"PRIXPTR, val & 0xFFFFFFFF);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -4560,110 +4561,268 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size)
|
||||
|
||||
|
||||
void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth)
|
||||
/* Test bit-field in ax. */
|
||||
/* Test bit-field in primary. */
|
||||
{
|
||||
unsigned EndBit = BitOffs + BitWidth;
|
||||
/* Since the end is inclusive and cannot be negative here, we subtract 1 from the sum */
|
||||
unsigned MSBit = BitOffs + BitWidth - 1U;
|
||||
unsigned Bytes = MSBit / CHAR_BITS + 1U - BitOffs / CHAR_BITS;
|
||||
unsigned HeadMask = (0xFF << (BitOffs % CHAR_BITS)) & 0xFF;
|
||||
unsigned TailMask = ((1U << (MSBit % CHAR_BITS + 1U)) - 1U) & 0xFF;
|
||||
unsigned UntestedBytes = ((1U << Bytes) - 1U) << (BitOffs / CHAR_BITS);
|
||||
|
||||
/* We don't use these flags for now. Could CF_NOKEEP be potentially interesting? */
|
||||
Flags &= ~CF_STYPEMASK;
|
||||
|
||||
/* If we need to do a test, then we avoid shifting (ASR only shifts one bit at a time,
|
||||
** so is slow) and just AND with the appropriate mask, then test the result of that.
|
||||
** so is slow) and just AND the head and tail bytes with the appropriate mask, then
|
||||
** OR the results with the rest bytes.
|
||||
*/
|
||||
|
||||
/* Avoid overly large shift on host platform. */
|
||||
if (EndBit == sizeof (unsigned long) * CHAR_BIT) {
|
||||
g_and (Flags | CF_CONST, (~0UL << BitOffs));
|
||||
} else {
|
||||
g_and (Flags | CF_CONST, ((1UL << EndBit) - 1) & (~0UL << BitOffs));
|
||||
if (Bytes == 1) {
|
||||
HeadMask = TailMask = HeadMask & TailMask;
|
||||
}
|
||||
|
||||
/* TODO: When long bit-fields are supported, an optimization to test only 3 bytes when
|
||||
** EndBit <= 24 is possible.
|
||||
*/
|
||||
g_test (Flags | CF_CONST);
|
||||
/* Get the head byte */
|
||||
switch (BitOffs / CHAR_BITS) {
|
||||
case 0:
|
||||
if (HeadMask == 0xFF && Bytes == 1) {
|
||||
AddCodeLine ("tax");
|
||||
UntestedBytes &= ~0x1;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (HeadMask != 0xFF || TailMask == 0xFF) {
|
||||
AddCodeLine ("txa");
|
||||
UntestedBytes &= ~0x2;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (HeadMask != 0xFF || TailMask == 0xFF) {
|
||||
AddCodeLine ("lda sreg");
|
||||
UntestedBytes &= ~0x4;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* In this case we'd have HeadMask == TailMask and only 1 byte, but anyways... */
|
||||
if (HeadMask != 0xFF || TailMask == 0xFF) {
|
||||
AddCodeLine ("lda sreg+1");
|
||||
UntestedBytes &= ~0x8;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Keep in mind that the head is NOT always "Byte 0" */
|
||||
if (HeadMask != 0xFF) {
|
||||
AddCodeLine ("and #$%02X", HeadMask);
|
||||
/* Abuse the "Byte 0" flag so that this head content will be saved by the routine */
|
||||
UntestedBytes |= 0x1;
|
||||
}
|
||||
|
||||
/* If there is only 1 byte to test, we have done with it */
|
||||
if (Bytes == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle the tail byte */
|
||||
if (TailMask != 0xFF) {
|
||||
/* If we have to do any more masking operation, register A will be used for that,
|
||||
** and its current content in it must be saved.
|
||||
*/
|
||||
if (UntestedBytes & 0x1) {
|
||||
AddCodeLine ("sta tmp1");
|
||||
}
|
||||
|
||||
/* Test the tail byte */
|
||||
switch (MSBit / CHAR_BITS) {
|
||||
case 1:
|
||||
AddCodeLine ("txa");
|
||||
UntestedBytes &= ~0x2;
|
||||
break;
|
||||
case 2:
|
||||
AddCodeLine ("lda sreg");
|
||||
UntestedBytes &= ~0x4;
|
||||
break;
|
||||
case 3:
|
||||
AddCodeLine ("lda sreg+1");
|
||||
UntestedBytes &= ~0x8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
AddCodeLine ("and #$%02X", TailMask);
|
||||
|
||||
if (UntestedBytes & 0x1) {
|
||||
AddCodeLine ("ora tmp1");
|
||||
}
|
||||
}
|
||||
|
||||
/* OR the rest bytes together, which could never need masking */
|
||||
if (UntestedBytes & 0x2) {
|
||||
AddCodeLine ("stx tmp1");
|
||||
AddCodeLine ("ora tmp1");
|
||||
}
|
||||
if (UntestedBytes & 0x4) {
|
||||
AddCodeLine ("ora sreg");
|
||||
}
|
||||
if (UntestedBytes & 0x8) {
|
||||
AddCodeLine ("ora sreg+1");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned,
|
||||
unsigned BitOffs, unsigned BitWidth)
|
||||
/* Extract bits from bit-field in ax. */
|
||||
/* Extract bits from bit-field in primary. */
|
||||
{
|
||||
unsigned EndBit = BitOffs + BitWidth;
|
||||
unsigned long ZeroExtendMask = 0; /* Zero if we don't need to zero-extend. */
|
||||
|
||||
/* Shift right by the bit offset; no code is emitted if BitOffs is zero */
|
||||
g_asr (Flags | CF_CONST, BitOffs);
|
||||
|
||||
/* Since we have now shifted down, we could do char ops when the width fits in a char, but we
|
||||
** also need to clear (or set) the high byte since we've been using CF_FORCECHAR up to now.
|
||||
*/
|
||||
unsigned Mask = (1U << BitWidth) - 1;
|
||||
|
||||
/* To zero-extend, we will and by the width if the field doesn't end on a char or
|
||||
** int boundary. If it does end on a boundary, then zeros will have already been shifted in,
|
||||
** but we need to clear the high byte for char. g_and emits no code if the mask is all ones.
|
||||
** This is here so the signed and unsigned branches can use it.
|
||||
*/
|
||||
unsigned ZeroExtendMask = 0; /* Zero if we don't need to zero-extend. */
|
||||
if (EndBit == CHAR_BITS) {
|
||||
/* We need to clear the high byte, since CF_FORCECHAR was set. */
|
||||
ZeroExtendMask = 0xFF;
|
||||
} else if (EndBit != INT_BITS) {
|
||||
ZeroExtendMask = (1U << BitWidth) - 1;
|
||||
} else if (EndBit != INT_BITS && EndBit != LONG_BITS) {
|
||||
ZeroExtendMask = shl_l (1UL, BitWidth) - 1UL;
|
||||
}
|
||||
|
||||
/* Handle signed bit-fields. */
|
||||
if (IsSigned) {
|
||||
/* Save .A because the sign-bit test will destroy it. */
|
||||
AddCodeLine ("tay");
|
||||
|
||||
/* Check sign bit */
|
||||
unsigned SignBitPos = BitWidth - 1U;
|
||||
unsigned SignBitByte = SignBitPos / CHAR_BITS;
|
||||
unsigned SignBitPosInByte = SignBitPos % CHAR_BITS;
|
||||
unsigned SignBitMask = 1U << SignBitPosInByte;
|
||||
|
||||
/* Move the correct byte to .A. This can be only .X for now,
|
||||
** but more cases will be needed to support long.
|
||||
*/
|
||||
switch (SignBitByte) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
AddCodeLine ("txa");
|
||||
break;
|
||||
default:
|
||||
FAIL ("Invalid Byte for sign bit");
|
||||
}
|
||||
|
||||
/* Test the sign bit */
|
||||
AddCodeLine ("and #$%02X", SignBitMask);
|
||||
unsigned ZeroExtendLabel = GetLocalLabel ();
|
||||
AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel));
|
||||
|
||||
/* Get back .A and sign-extend if required; operating on the full result needs
|
||||
** to sign-extend into the high byte, too.
|
||||
*/
|
||||
AddCodeLine ("tya");
|
||||
g_or (FullWidthFlags | CF_CONST, ~Mask);
|
||||
|
||||
/* We can generate a branch, instead of a jump, here because we know
|
||||
** that only a few instructions will be put between here and where
|
||||
** DoneLabel will be defined.
|
||||
*/
|
||||
unsigned DoneLabel = GetLocalLabel ();
|
||||
g_branch (DoneLabel);
|
||||
|
||||
/* Get back .A, then zero-extend. We need to duplicate the TYA, rather than move it before
|
||||
** the branch to share with the other label, because TYA changes some condition codes.
|
||||
*/
|
||||
g_defcodelabel (ZeroExtendLabel);
|
||||
AddCodeLine ("tya");
|
||||
|
||||
/* Zero the upper bits, the same as the unsigned path. */
|
||||
if (ZeroExtendMask != 0) {
|
||||
g_and (FullWidthFlags | CF_CONST, ZeroExtendMask);
|
||||
}
|
||||
/* The universal trick is:
|
||||
** x = bits & bit_mask
|
||||
** m = 1 << (bit_width - 1)
|
||||
** r = (x ^ m) - m
|
||||
** which works for long as well.
|
||||
*/
|
||||
|
||||
g_defcodelabel (DoneLabel);
|
||||
if (SignBitByte + 1U == sizeofarg (FullWidthFlags)) {
|
||||
/* We can just sign-extend on the high byte if it is the only affected one */
|
||||
unsigned char SignBitMask = (1UL << SignBitPosInByte) & 0xFF;
|
||||
unsigned char Mask = ((2UL << (SignBitPos % CHAR_BITS)) - 1UL) & 0xFF;
|
||||
|
||||
/* Move the correct byte to .A */
|
||||
switch (SignBitByte) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
AddCodeLine ("tay");
|
||||
AddCodeLine ("txa");
|
||||
break;
|
||||
case 3:
|
||||
AddCodeLine ("tay");
|
||||
AddCodeLine ("lda sreg+1");
|
||||
break;
|
||||
default:
|
||||
FAIL ("Invalid Byte for sign bit");
|
||||
}
|
||||
|
||||
/* Use .A to do the ops on the correct byte */
|
||||
AddCodeLine ("and #$%02X", Mask);
|
||||
AddCodeLine ("eor #$%02X", SignBitMask);
|
||||
AddCodeLine ("sec");
|
||||
AddCodeLine ("sbc #$%02X", SignBitMask);
|
||||
|
||||
/* Move the correct byte from .A */
|
||||
switch (SignBitByte) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
AddCodeLine ("tax");
|
||||
AddCodeLine ("tya");
|
||||
break;
|
||||
case 3:
|
||||
AddCodeLine ("sta sreg+1");
|
||||
AddCodeLine ("tya");
|
||||
break;
|
||||
default:
|
||||
FAIL ("Invalid Byte for sign bit");
|
||||
}
|
||||
} else {
|
||||
unsigned long SignBitMask = 1UL << SignBitPos;
|
||||
unsigned long Mask = (2UL << SignBitPos) - 1UL;
|
||||
g_and (FullWidthFlags | CF_CONST, Mask);
|
||||
g_xor (FullWidthFlags | CF_CONST, SignBitMask);
|
||||
g_dec (FullWidthFlags | CF_CONST, SignBitMask);
|
||||
}
|
||||
} else {
|
||||
unsigned char SignBitMask = (1UL << SignBitPosInByte) & 0xFF;
|
||||
unsigned ZeroExtendLabel = GetLocalLabel ();
|
||||
|
||||
/* Save .A because the sign-bit test will destroy it. */
|
||||
AddCodeLine ("tay");
|
||||
|
||||
/* Move the correct byte to .A */
|
||||
switch (SignBitByte) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
AddCodeLine ("txa");
|
||||
break;
|
||||
case 3:
|
||||
AddCodeLine ("lda sreg+1");
|
||||
break;
|
||||
default:
|
||||
FAIL ("Invalid Byte for sign bit");
|
||||
}
|
||||
|
||||
/* Test the sign bit */
|
||||
AddCodeLine ("and #$%02X", SignBitMask);
|
||||
AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel));
|
||||
|
||||
if (SignBitByte + 1U == sizeofarg (FullWidthFlags)) {
|
||||
/* We can just sign-extend on the high byte if it is the only affected one */
|
||||
unsigned char Mask = ~((2UL << (SignBitPos % CHAR_BITS)) - 1UL) & 0xFF;
|
||||
|
||||
/* Use .A to do the ops on the correct byte */
|
||||
switch (SignBitByte) {
|
||||
case 0:
|
||||
AddCodeLine ("tya");
|
||||
AddCodeLine ("ora #$%02X", Mask);
|
||||
/* We could jump over the following tya instead, but that wouldn't be faster
|
||||
** than taking this extra tay and then the tya.
|
||||
*/
|
||||
AddCodeLine ("tay");
|
||||
break;
|
||||
case 1:
|
||||
AddCodeLine ("txa");
|
||||
AddCodeLine ("ora #$%02X", Mask);
|
||||
AddCodeLine ("tax");
|
||||
break;
|
||||
case 3:
|
||||
AddCodeLine ("lda sreg+1");
|
||||
AddCodeLine ("ora #$%02X", Mask);
|
||||
AddCodeLine ("sta sreg+1");
|
||||
break;
|
||||
default:
|
||||
FAIL ("Invalid Byte for sign bit");
|
||||
}
|
||||
} else {
|
||||
/* Since we are going to get back .A later anyways, we may just do the op on the
|
||||
** higher bytes with whatever content currently in it.
|
||||
*/
|
||||
unsigned long Mask = ~((2UL << SignBitPos) - 1UL);
|
||||
g_or (FullWidthFlags | CF_CONST, Mask);
|
||||
}
|
||||
|
||||
/* Get back .A. We need to duplicate the TYA, rather than move it before
|
||||
** the branch to share with the other label, because TYA changes some condition codes.
|
||||
*/
|
||||
g_defcodelabel (ZeroExtendLabel);
|
||||
AddCodeLine ("tya");
|
||||
}
|
||||
} else {
|
||||
/* Unsigned bit-field, needs only zero-extension. */
|
||||
if (ZeroExtendMask != 0) {
|
||||
|
@ -37,10 +37,10 @@
|
||||
#define CODEGEN_H
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* common */
|
||||
#include "coll.h"
|
||||
#include "inttypes.h"
|
||||
|
||||
/* cc65 */
|
||||
#include "segments.h"
|
||||
@ -217,7 +217,7 @@ void g_reglong (unsigned Flags);
|
||||
unsigned g_typeadjust (unsigned lhs, unsigned rhs);
|
||||
/* Adjust the integer operands before doing a binary operation. lhs is a flags
|
||||
** value, that corresponds to the value on TOS, rhs corresponds to the value
|
||||
** in (e)ax. The return value is the the flags value for the resulting type.
|
||||
** in (e)ax. The return value is the flags value for the resulting type.
|
||||
*/
|
||||
|
||||
unsigned g_typecast (unsigned lhs, unsigned rhs);
|
||||
@ -271,7 +271,7 @@ void g_restore_regvars (int StackOffs, int RegOffs, unsigned Bytes);
|
||||
|
||||
|
||||
|
||||
void g_getimmed (unsigned Flags, unsigned long Val, long Offs);
|
||||
void g_getimmed (unsigned Flags, uintptr_t Val, long Offs);
|
||||
/* Load a constant into the primary register */
|
||||
|
||||
void g_getstatic (unsigned Flags, uintptr_t Label, long Offs);
|
||||
@ -461,7 +461,7 @@ void g_ge (unsigned flags, unsigned long val);
|
||||
void g_res (unsigned n);
|
||||
/* Reserve static storage, n bytes */
|
||||
|
||||
void g_defdata (unsigned flags, unsigned long val, long offs);
|
||||
void g_defdata (unsigned flags, uintptr_t val, long offs);
|
||||
/* Define data with the size given in flags */
|
||||
|
||||
void g_defbytes (const void* bytes, unsigned count);
|
||||
@ -486,11 +486,11 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size);
|
||||
/*****************************************************************************/
|
||||
|
||||
void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth);
|
||||
/* Test bit-field in ax. */
|
||||
/* Test bit-field in primary. */
|
||||
|
||||
void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned,
|
||||
unsigned BitOffs, unsigned BitWidth);
|
||||
/* Extract bits from bit-field in ax. */
|
||||
/* Extract bits from bit-field in primary. */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Switch statement */
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "funcdesc.h"
|
||||
#include "function.h"
|
||||
#include "global.h"
|
||||
#include "initdata.h"
|
||||
#include "input.h"
|
||||
#include "litpool.h"
|
||||
#include "macrotab.h"
|
||||
|
@ -309,10 +309,10 @@ unsigned OptCmp1 (CodeSeg* S)
|
||||
|
||||
/* Insert the ora instead */
|
||||
X = NewCodeEntry (OP65_ORA, L[0]->AM, L[0]->Arg, 0, L[0]->LI);
|
||||
CS_InsertEntry (S, X, I);
|
||||
CS_InsertEntry (S, X, I+3);
|
||||
|
||||
/* Remove all other instructions */
|
||||
CS_DelEntries (S, I+1, 3);
|
||||
CS_DelEntries (S, I, 3);
|
||||
|
||||
/* Remember, we had changes */
|
||||
++Changes;
|
||||
|
@ -551,6 +551,24 @@ unsigned long GetIntegerTypeMax (const Type* Type)
|
||||
|
||||
|
||||
|
||||
static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth)
|
||||
/* Return the size of the smallest integer type that may have BitWidth bits */
|
||||
{
|
||||
/* Since all integer types supported in cc65 for bit-fields have sizes that
|
||||
** are powers of 2, we can just use this bit-twiddling trick.
|
||||
*/
|
||||
unsigned V = (int)(BitWidth - 1U) / (int)CHAR_BITS;
|
||||
V |= V >> 1;
|
||||
V |= V >> 2;
|
||||
V |= V >> 4;
|
||||
V |= V >> 8;
|
||||
V |= V >> 16;
|
||||
|
||||
/* Return the result size */
|
||||
return V + 1U;
|
||||
}
|
||||
|
||||
|
||||
static unsigned TypeOfBySize (unsigned Size)
|
||||
/* Get the code generator replacement type of the object by its size */
|
||||
{
|
||||
@ -591,8 +609,7 @@ const Type* GetUnderlyingType (const Type* Type)
|
||||
** bit-field, instead of the type used in the declaration, the truly
|
||||
** underlying of the bit-field.
|
||||
*/
|
||||
unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1;
|
||||
switch (Size) {
|
||||
switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
|
||||
case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break;
|
||||
case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break;
|
||||
case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break;
|
||||
@ -646,8 +663,7 @@ TypeCode GetUnderlyingTypeCode (const Type* Type)
|
||||
** bit-field, instead of the type used in the declaration, the truly
|
||||
** underlying of the bit-field.
|
||||
*/
|
||||
unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1;
|
||||
switch (Size) {
|
||||
switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
|
||||
case SIZEOF_CHAR: Underlying = T_CHAR; break;
|
||||
case SIZEOF_INT: Underlying = T_INT; break;
|
||||
case SIZEOF_LONG: Underlying = T_LONG; break;
|
||||
@ -663,6 +679,39 @@ TypeCode GetUnderlyingTypeCode (const Type* Type)
|
||||
|
||||
|
||||
|
||||
const Type* GetBitFieldChunkType (const Type* Type)
|
||||
/* Get the type needed to operate on the byte chunk containing the bit-field */
|
||||
{
|
||||
unsigned ChunkSize;
|
||||
if ((Type->A.B.Width - 1U) / CHAR_BITS ==
|
||||
(Type->A.B.Offs + Type->A.B.Width - 1U) / CHAR_BITS) {
|
||||
/* T bit-field fits within its underlying type */
|
||||
return GetUnderlyingType (Type);
|
||||
}
|
||||
|
||||
ChunkSize = GetBitFieldMinimalTypeSize (Type->A.B.Offs + Type->A.B.Width);
|
||||
if (ChunkSize < SizeOf (Type + 1)) {
|
||||
/* The end of the bit-field is offset by some bits so that it requires
|
||||
** more bytes to be accessed as a whole than its underlying type does.
|
||||
** Note: In cc65 the bit offset is always less than CHAR_BITS.
|
||||
*/
|
||||
switch (ChunkSize) {
|
||||
case SIZEOF_CHAR: return IsSignSigned (Type) ? type_schar : type_uchar;
|
||||
case SIZEOF_INT: return IsSignSigned (Type) ? type_int : type_uint;
|
||||
case SIZEOF_LONG: return IsSignSigned (Type) ? type_long : type_ulong;
|
||||
default: return IsSignSigned (Type) ? type_int : type_uint;
|
||||
}
|
||||
}
|
||||
|
||||
/* We can always use the declarartion integer type as the chunk type.
|
||||
** Note: A bit-field will not occupy bits located in bytes more than that
|
||||
** of its declaration type in cc65. So this is OK.
|
||||
*/
|
||||
return Type + 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned SizeOf (const Type* T)
|
||||
/* Compute size of object represented by type array. */
|
||||
{
|
||||
@ -967,9 +1016,18 @@ const Type* IntPromotion (const Type* T)
|
||||
*/
|
||||
|
||||
if (IsTypeBitField (T)) {
|
||||
/* The standard rule is OK for now as we don't support bit-fields with widths > 16.
|
||||
/* As we now support long bit-fields, we need modified rules for them:
|
||||
** - If an int can represent all values of the bit-field, the bit-field is converted
|
||||
** to an int;
|
||||
** - Otherwise, if an unsigned int can represent all values of the bit-field, the
|
||||
** bit-field is converted to an unsigned int;
|
||||
** - Otherwise, the bit-field will have its declared integer type.
|
||||
** These rules are borrowed from C++ and seem to be consistent with GCC/Clang's.
|
||||
*/
|
||||
return T->A.B.Width >= INT_BITS && IsSignUnsigned (T) ? type_uint : type_int;
|
||||
if (T->A.B.Width > INT_BITS) {
|
||||
return IsSignUnsigned (T) ? type_ulong : type_long;
|
||||
}
|
||||
return T->A.B.Width == INT_BITS && IsSignUnsigned (T) ? type_uint : type_int;
|
||||
} else if (IsTypeChar (T)) {
|
||||
/* An integer can represent all values from either signed or unsigned char, so convert
|
||||
** chars to int.
|
||||
@ -1127,6 +1185,15 @@ Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth)
|
||||
|
||||
|
||||
|
||||
int IsTypeFragBitField (const Type* T)
|
||||
/* Return true if this is a bit-field that shares byte space with other fields */
|
||||
{
|
||||
return IsTypeBitField (T) &&
|
||||
(T->A.B.Offs != 0 || T->A.B.Width != CHAR_BITS * SizeOf (T));
|
||||
}
|
||||
|
||||
|
||||
|
||||
int IsClassObject (const Type* T)
|
||||
/* Return true if this is a fully described object type */
|
||||
{
|
||||
|
@ -313,6 +313,9 @@ TypeCode GetUnderlyingTypeCode (const Type* Type);
|
||||
** Return TCode if it is not scalar.
|
||||
*/
|
||||
|
||||
const Type* GetBitFieldChunkType (const Type* Type);
|
||||
/* Get the type needed to operate on the byte chunk containing the bit-field */
|
||||
|
||||
unsigned SizeOf (const Type* T);
|
||||
/* Compute size of object represented by type array. */
|
||||
|
||||
@ -556,6 +559,9 @@ INLINE int IsTypeBitField (const Type* T)
|
||||
# define IsTypeBitField(T) (IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T))
|
||||
#endif
|
||||
|
||||
int IsTypeFragBitField (const Type* T);
|
||||
/* Return true if this is a bit-field that shares byte space with other fields */
|
||||
|
||||
#if defined(HAVE_INLINE)
|
||||
INLINE int IsTypeStruct (const Type* T)
|
||||
/* Return true if this is a struct type */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user