Compare commits

...

25 Commits

Author SHA1 Message Date
Wayne Warthen
08942fb944 Support Duodyne SelfHost UART 2023-11-20 17:01:53 -08:00
Wayne Warthen
5dc724785b Completed CH37x Driver
The ch.asm driver now supports SD Card operations (only possible for CH376).
2023-11-19 14:48:41 -08:00
Wayne Warthen
ce17be9ba6 CP/M 3 RAM Check
- Check to ensure that we have enough RAM banks for banked CP/M 3 startup.  If not, message and return to boot loader.
2023-11-18 16:58:23 -08:00
Wayne Warthen
badca621ba Merge pull request #372 from dylanhall/dev
Allow override of secondary console front panel switch selection
2023-11-18 16:54:50 -08:00
Dylan Hall
291cdd2c03 Use SECCON to override default secondary console selection 2023-11-17 17:47:01 +13:00
Wayne Warthen
888d9879c9 Preliminary Support for CH37x SD Cards
- Currently operates as a read only disk device.
2023-11-16 19:39:09 -08:00
Dylan Hall
39446d5e4f Add SECCON to config files 2023-11-17 16:36:29 +13:00
Wayne Warthen
af8385fba8 Fix IM1 Handling for Z180 - Credit to Dylan Hall
- Z180 code failed to initialize interrupt vector registers for IM1 startup
- Updated bad interrupt messaging to avoid interrupt stack overflow
2023-11-15 12:48:55 -08:00
Wayne Warthen
10ff97b6c6 ACIA Interrupt Fix & ROMless APPBOOT Fix
- ACIA driver was not properly returning ZF to indicate if it handled an interrupt.
- APPBOOT was failing on ROMless systems because it was copying the HBIOS code overtop of itself.
2023-11-09 14:37:46 -08:00
Wayne Warthen
7e9191f3ef Update ps2info.asm
- Updated PS2INFO to handle extraneous 0x00 byte after reset command.
2023-11-07 18:30:17 -08:00
Wayne Warthen
a67b1ecd0a Fix RTC Init on ROMless Restart
- On ROMless restart, the RTC was not being included in the boot messages.
2023-10-31 14:54:07 -07:00
Wayne Warthen
4012ee7775 Update User Guide
Based on input from Issue #364, I have attempted to clarify a few areas on the User Guide:
- Recommendation to only use ROM OSes only for limited purposes
- Added a more detailed description of the automatic drive assignment algorithm
- Added more detail on batch file auto-submission
2023-10-31 13:03:09 -07:00
Wayne Warthen
150ca6b50c Fix .gitignore 2023-10-30 15:03:59 -07:00
Wayne Warthen
6af84e9ad8 Update Makefile
- Fix Makefile
2023-10-30 14:29:02 -07:00
Wayne Warthen
6bcad122cd Add Z1RCC Support
- Added build support for Bill Chen's Z1RCC.
- Thanks and credit to Bill for supplying the bulk of the build changes.
2023-10-30 14:14:11 -07:00
Wayne Warthen
003481410f Update UNARC to Universal UNARC from Lars Nelson
- Credit and thanks to Lars Nelson for providing an enhanced version of UNARC.
2023-10-30 12:07:26 -07:00
Wayne Warthen
9a1c3f7929 Minor Fix in SPK Driver and Tick Counter Space Reservation
- Fix ensures that the speaker control bit is set the same as it was initially after playing a tone.
- Reserve 2 bytes in the proxy for a platform dependent tick counter value.  Currently for HEATH platform.
2023-10-30 11:41:31 -07:00
Wayne Warthen
ef536750ea Makefile Improvements
These changes improve the chances of a make operation actually failing when a nested error occurs.
2023-10-24 13:25:32 -07:00
Wayne Warthen
347a15a3b6 Build Script Updates
- Minor update to GitHub build scripts
2023-10-23 18:30:40 -07:00
Wayne Warthen
1e5b38c251 PS2INFO Update & OpenSpin Conversion
- Added support for Duodyne to PS2INFO application.
- Switched all build paths to consistently use OpenSpin since it appears to be compatible with all build environments supported by RomWBW.
2023-10-23 18:07:42 -07:00
Wayne Warthen
29f93fb153 Enable CH and LPT Drivers for Duodyne 2023-10-19 17:23:08 -07:00
Wayne Warthen
b0975745df Bump Version 2023-10-19 15:47:45 -07:00
Wayne Warthen
163460856a Add Real Time Clock Section to User Guide, Issue #368 2023-10-19 15:03:49 -07:00
Wayne Warthen
3ce9246904 Update FLASH to v1.3.7
Thanks and credit to Will Sowerbutts for contributing and maintaining this critical utility!
2023-10-19 14:09:45 -07:00
Wayne Warthen
1a021e02b9 Enable PIO Support for Duodyne 2023-10-16 15:51:14 -07:00
126 changed files with 17337 additions and 782 deletions

View File

@@ -26,7 +26,7 @@ jobs:
run: |
export TZ='America/Los_Angeles'
sudo apt-get install srecord
make dist
make distlog
rm -rf .git*
- name: List Output
@@ -58,7 +58,7 @@ jobs:
run: |
export TZ='America/Los_Angeles'
brew install srecord
make dist
make distlog
rm -rf .git*
- name: List Output

View File

@@ -19,7 +19,7 @@ jobs:
export TZ='America/Los_Angeles'
sudo apt-get install libncurses-dev
sudo apt-get install srecord
make dist
make distlog
rm -rf .git*
- name: Create Package Archive

4
.gitignore vendored
View File

@@ -95,8 +95,8 @@ Tools/unix/zx/zx
!Source/ZPM3/*.[Cc][Oo][Mm]
!Source/ZSDOS/*.[Cc][Oo][Mm]
!Source/ZRC/*.bin
!Source/ZZRC/*.bin
!Source/ZZRC/*.hex
!Source/Z1RCC/*.bin
!Source/ZZRCC/*.bin
!Tools/cpm/**
!Tools/unix/zx/*
!Tools/zx/*

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,8 @@
.PHONY: tools source clean clobber diff dist
.ONESHELL:
.SHELLFLAGS = -cex
all: tools source
tools:
@@ -20,6 +23,9 @@ diff:
$(MAKE) --directory Source diff
dist:
$(MAKE) ROM_PLATFORM=dist 2>&1 | tee make.log
$(MAKE) --directory Source clean
$(MAKE) ROM_PLATFORM=dist
$(MAKE) --directory Tools clean
$(MAKE) --directory Source clean
distlog:
$(MAKE) dist 2>&1 | tee make.log

View File

@@ -3,7 +3,7 @@
**RomWBW ReadMe** \
Version 3.4 \
Wayne Warthen ([wwarthen@gmail.com](mailto:wwarthen@gmail.com)) \
13 Oct 2023
31 Oct 2023
# Overview

View File

@@ -1,6 +1,6 @@
RomWBW ReadMe
Wayne Warthen (wwarthen@gmail.com)
13 Oct 2023
31 Oct 2023

View File

@@ -7,20 +7,24 @@
; keyboard, and mouse.
;
; WBW 2022-03-28: Add menu driven port selection
; Add support for RHYOPHYRE
; Add support for Rhyophyre
; WBW 2022-04-01: Add menu for test functions
; WBW 2022-04-02: Fix prtchr register saving/recovery
; WBW 2023-10-19: Add support for Duodyne
;
;=======================================================================
;
; PS/2 Keyboard/Mouse controller port addresses (adjust as needed)
;
; MBC:
; Nhyodyne:
iocmd_mbc .equ $E3 ; PS/2 controller command port address
iodat_mbc .equ $E2 ; PS/2 controller data port address
; RPH:
; Rhyophyre:
iocmd_rph .equ $8D ; PS/2 controller command port address
iodat_rph .equ $8C ; PS/2 controller data port address
; Duodyne:
iocmd_duo .equ $4D ; PS/2 controller command port address
iodat_duo .equ $4C ; PS/2 controller data port address
;
cpumhz .equ 8 ; for time delay calculations (not critical)
;
@@ -77,10 +81,12 @@ setup1:
jr z,setup1
call upcase
call prtchr
cp '1' ; MBC
cp '1' ; Nhyodyne
jr z,setup_mbc
cp '2' ; RHYOPHYRE
cp '2' ; Rhyophyre
jr z,setup_rph
cp '3' ; Duodyne
jr z,setup_duo
cp 'X'
jr z,exit
jr setup
@@ -101,6 +107,14 @@ setup_rph:
ld de,str_rph
jr setup2
;
setup_duo:
ld a,iocmd_duo
ld (iocmd),a
ld a,iodat_duo
ld (iodat),a
ld de,str_duo
jr setup2
;
setup2:
call prtstr
call crlf2
@@ -181,6 +195,12 @@ test_kbd:
;
call ctlr_test
jr nz,test_kbd_fail
;
ld a,$20 ; kbd enabled, mse disabled, no ints
call ctlr_setup
jr nz,test_kbd_fail
;
call ctlr_flush
;
call test_kbd_basic
jr nz,test_kbd_fail
@@ -228,9 +248,13 @@ test_mse:
ld a,$10 ; kbd disabled, mse enabled, no ints
call ctlr_setup
jr nz,test_mse_fail
;
call ctlr_flush
;
call mse_reset
jr nz,test_mse_fail
;
call ctlr_flush
;
call mse_ident
jr nz,test_mse_fail
@@ -262,15 +286,21 @@ test_kbdmse:
ld a,$00 ; kbd enabled, mse enabled, no ints
call ctlr_setup
jr nz,test_kbdmse_fail
;
call ctlr_flush
;
call kbd_reset
jr nz,test_kbdmse_fail
;
call ctlr_flush
;
ld a,2
call kbd_setsc
;
call mse_reset
jr nz,test_kbdmse_fail
;
call ctlr_flush
;
call mse_stream
jr nz,test_kbdmse_fail
@@ -290,15 +320,13 @@ test_kbdmse_fail:
; inventory the supported scan code sets.
;
test_kbd_basic:
ld a,$20 ; Xlat off for this checking
call ctlr_setup
ret nz
;
call kbd_reset
ret nz
;
call ctlr_flush
;
call kbd_ident
;ret nz
ret nz
;
ld b,3 ; Loop control, 3 scan code sets
ld c,1 ; Current scan code number
@@ -436,6 +464,19 @@ ctlr_setup:
xor a
ret
;
; Flush incoming data buffer
;
ctlr_flush:
call crlf2
ld de,str_ctlr_flush
call prtstr
ctlr_flush1:
call delay ; small delay
call check_read ; data pending?
ret nz ; return if nothing there
call get_data_dbg ; get and discard byte
jr ctlr_flush1 ; loop
;
; Perform a keyboard reset
;
kbd_reset:
@@ -612,13 +653,17 @@ mse_reset:
call crlf2
ld de,str_mse_reset
call prtstr
ld a,$f2 ; Identify mouse command
ld a,$ff ; Identify mouse command
call put_data_mse_dbg
jp c,err_ctlr_to ; handle controller error
call get_data_dbg
jp c,err_ctlr_to ; handle controller error
cp $fa ; Is it an ack as expected?
jp nz,err_mse_reset
call get_data_dbg
jp c,err_ctlr_to ; handle controller error
cp $aa ; Success?
jp nz,err_mse_reset
call crlf
ld de,str_mse_reset_ok
call prtstr
@@ -634,18 +679,61 @@ mse_ident:
ld a,$f2 ; Identify mouse command
call put_data_mse_dbg
jp c,err_ctlr_to ; handle controller error
mse_ident0:
call get_data_dbg
jp c,err_ctlr_to ; handle controller error
;cp $00 ; extraneous?
;jr z,mse_ident0 ; ignore it, get another
cp $fa ; Is it an ack as expected?
jp nz,err_mse_ident
call get_data_dbg
jp c,err_ctlr_to ; handle controller error
; Now we need to receive 0-2 bytes. There is no way to know
; how many are coming, so we receive bytes until there is a
; timeout error. Timeout is shortened here so that we don't
; have to wait seconds for the routine to complete normally.
; A short timeout is more than sufficient here.
ld ix,workbuf
ld a,(timeout) ; save current timeout
push af
ld a,stimout ; set a short timeout
ld (timeout),a
ld b,8 ; buf max
ld c,0 ; buf len
mse_ident1:
push bc
call get_data_dbg
pop bc
jr c,mse_ident2
ld (ix),a
inc ix
inc c
djnz mse_ident1
mse_ident2:
pop af ; restore original timeout
ld (timeout),a
call crlf
ld de,str_mse_ident_disp
call prtstr
pop af
call prtdecb
ld a,'['
call prtchr
ld ix,workbuf
ld a,c ; bytes to print
or a ; check for zero
jr z,mse_ident4 ; handle zero
ld b,a ; setup loop counter
jr mse_ident3a
mse_ident3:
ld a,','
call prtchr
mse_ident3a:
ld a,(ix)
call prthex
inc ix
djnz mse_ident3
mse_ident4:
ld a,']'
call prtchr
xor a
ret
;
@@ -658,8 +746,13 @@ mse_stream:
ld a,$f4 ; Stream packets cmd
call put_data_mse_dbg
jp c,err_ctlr_to ; handle controller error
mse_stream0:
call get_data_dbg
jp c,err_ctlr_to ; handle controller error
;cp $00 ; extraneous?
;jr z,mse_stream0 ; ignore it, get another
cp $FA ; Is it an ack as expected?
jp nz,err_mse_stream
xor a
@@ -1344,14 +1437,16 @@ delay1:
; Constants
;=======================================================================
;
str_banner .db "PS/2 Keyboard/Mouse Information v0.6a, 2-Apr-2022",0
str_banner .db "PS/2 Keyboard/Mouse Information v0.8, 6-Nov-2023",0
str_hwmenu .db "PS/2 Controller Port Options:\r\n\r\n"
.db " 1 - MBC\r\n"
.db " 2 - RHYOPHYRE\r\n"
.db " 1 - Nhyodyne\r\n"
.db " 2 - Rhyophyre\r\n"
.db " 3 - Duodyne\r\n"
.db " X - Exit Application\r\n"
.db "\r\nSelection? ",0
str_mbc .db "MBC",0
str_rph .db "RHYOPHYRE",0
str_mbc .db "Nhyodyne",0
str_rph .db "Rhyophyre",0
str_duo .db "Duodyne",0
str_menu .db "PS/2 Testing Options:\r\n\r\n"
.db " C - Test PS/2 Controller\r\n"
.db " K - Test PS/2 Keyboard\r\n"
@@ -1382,6 +1477,7 @@ str_trans_off .db "***** Testing Keyboard with Scan Code Translation DISABLED *
str_trans_on .db "***** Testing Keyboard with Scan Code Translation ENABLED *****",0
str_basic_mse .db "***** Basic Mouse Tests *****",0
str_kbdmse .db "***** Test All Devices Combined *****",0
str_ctlr_flush .db "Flushing controller input buffer",0
str_kbd_reset .db "Attempting Keyboard Reset",0
str_kbd_reset_ok .db "Keyboard Reset OK",0
str_err_kbd_reset .db "Keyboard Reset Failed",0

View File

@@ -8,6 +8,7 @@ call BuildShared || exit /b
call BuildImages || exit /b
call BuildROM %* || exit /b
call BuildZRC || exit /b
call BuildZ1RCC || exit /b
call BuildZZRCC || exit /b
if "%1" == "dist" (

4
Source/BuildZ1RCC.cmd Normal file
View File

@@ -0,0 +1,4 @@
@echo off
setlocal
pushd Z1RCC && call Build || exit /b & popd

View File

@@ -13,7 +13,7 @@
extrn @dtbl,@ctbl
extrn @date,@hour,@min,@sec
extrn @srch1
extrn @hbbio
extrn @hbbio,@hbusr
extrn addhla
extrn phex16, phex8
extrn cin, cout
@@ -46,7 +46,7 @@ tpa$bank equ 0
; Clone page zero from bank 0 to additional banks
ld b,2 ; last bank
ld c,0 ; src bank
init$2:
init$1:
push bc ; save bank id's
call ?xmove ; set src/dest banks
ld bc,0100h ; size is one page
@@ -54,7 +54,7 @@ init$2:
ld de,0 ; src adr is 0
call ?move ; do it
pop bc ; restore bank id's
djnz init$2 ; loop till done
djnz init$1 ; loop till done
endif
@@ -62,6 +62,35 @@ init$2:
ld hl,signon$msg ; signon message
call ?pmsg ; print it
if banked
; Confirm that HBIOS is configured with enough RAM banks
; to accommodate banked version of CP/M 3. We use 2
; additional banks which live below the user bank. So we
; check that the these don't overlap with the RomWBW HBIOS
; bank.
ld bc,0F8F2h ; HBIOS GET BNKINFO
call 0FFF0h ; D: BIOS Bank, E: User Bank
ld a,d
ld (@hbbio),a
ld a,e
ld (@hbusr),a
sub 3 ; 2 extra banks (+1 for compare)
cp d ; lowest cpm bank - hbios bank
jr nc,init$2 ; continue if space available
ld hl,noram$msg ; signon message
call ?pmsg ; print it
ld b,0F0h ; HBIOS system reset
ld c,1h ; reset type warm (back to loader)
call 0FFFFh ; do it
endif
init$2:
; Check for HBIOS/CBIOS mismatch
ld b,0F1h ; hbios version
rst 08 ; do it, de=maj/min/up/pat
@@ -731,6 +760,10 @@ clrflg db 0 ; RAM disk cleared flag
clr$msg db 'RAM Disk Initialized',13,10,13,10,0
vermis$msg db 7,'*** WARNING: HBIOS/CBIOS Version Mismatch ***',13,10,13,10,0
if banked
noram$msg db 7,'*** ERROR: Insufficient RAM for banked CP/M 3 ***',13,10,13,10,0
endif
if zpm
signon$msg db 13,10,'ZPM3'

View File

@@ -22,4 +22,5 @@ pushd Prop && call Clean & popd
pushd RomDsk && call Clean & popd
pushd Doc && call Clean & popd
pushd ZRC && call Clean & popd
pushd Z1RCC && call Clean & popd
pushd ZZRCC && call Clean & popd

View File

@@ -201,6 +201,7 @@ below, **carefully** pick the appropriate ROM image for your hardware.
| [Nhyodyne Z80 MBC]^1^ | MBC | MBC_std.rom | 38400 |
| [Rhyophyre Z180 SBC]^1^ | - | RPH_std.rom | 38400 |
| [Z80 ZRC CPU Module]^7^ | RCBus | RCZ80_zrc.rom | 115200 |
| [Z180 Z1RCC CPU Module]^7^ | RCBus | RCZ180_z1rcc.rom | 115200 |
| [Z280 ZZRCC CPU Module]^7^ | RCBus | RCZ280_zzrc.rom | 115200 |
| [Z280 ZZ80MB SBC]^7^ | RCBus | RCZ280_zz80mb.rom | 115200 |
| [Z80-Retro SBC]^8^ | - | Z80RETRO_std.rom | 38400 |
@@ -595,6 +596,14 @@ technique is useful when:
The RAM disk and ROM disk drives will be available even if you have
no physical disk devices attached to your system.
Booting an operating system from ROM is not intended as a way to use
your operating system on a long-term basis. The ROM disk has only
a small subset of the operating system files. Additionally, you
cannot easily customize your ROM disk because you cannot write to it.
For any significant use of an operating system, you should boot directly
to the disk/slice that contains the complete operating system. This
is described in the next section.
## Starting Operating Systems from Disk
In order to make use of the more sophisticated operating systems
@@ -618,7 +627,7 @@ has been assigned to the disk and slice you selected to boot.
If you receive the error message "Disk not bootable!", you have
either failed to properly initialize the disk and slice requested
or you have selected the wrong disk/slice.
or you have selected an invalid/unavailable disk/slice.
The following example shows a disk boot into the first slice of disk
unit 4 which happens to be the CP/M 2.2 operating system on this disk.
@@ -696,6 +705,26 @@ have specified. It does not know what operating system is at that
location. The layout of operating systems on disk media is described in
the Using Disks section of this document.
### Auto-Submit Batch Files
All of the operating systems supplied with RomWBW have the ability to
execute a "batch" of commands by creating a batch submission file
containing the commands to be executed. The specifics of using
batch files in a specific operating system is covered in its specific
documentation.
At boot, the operating system will look for a specific batch file
(`PROFILE.SUB` for CP/M 2.2 and 3) on the boot drive and execute that
batch file automatically. This allows you to automatically customize
your operating system with any commands desired at boot. CP/M 2.2 did
not originally have the ability to automatically excute a batch file at
boot, but the CBIOS in RomWBW has added this capability.
Since RomWBW can utilize many disk slices, it is very easy to create
slices for specific workflows (editing, software development, games,
etc.). You can then just boot to the slice that is optimized for the
task you want to perform.
## System Management
### Listing Disk Device Inventory
@@ -901,7 +930,7 @@ Configuring Drives...
```
You will probably see more drive letters than this. The drive letter
assignment process is described below in the Drive Letter Assignment
assignment process is described below in the [Drive Letter Assignment]
section. Be aware that RomWBW will only assign drive letters to disk
interfaces that actually have media in them. If you do not see drive
letters assigned as expected, refer to the prior system boot messages
@@ -992,6 +1021,69 @@ rest of the drive letters will be offset to accommodate this. This is
done because most legacy operating systems expect that A: will be the
boot drive.
### Default Drive Letter Assignment
As shown above, when an operating system is booted, RomWBW will
automatically assign drive letters to physical disk devices. The
assignment process varies depending on: 1) the drive/slice you choose to
boot from, and 2) the number and type of physical drives in your
system.
If you boot an operating system from ROM, then the first two drive
letters will be assigned to your RAM disk (A:) and your ROM disk (B:).
It may seem odd that the RAM disk is assigned to A: in this case. The
reason for this is to accommodate certain functions that require that A:
be a writable disk drive. For example, A: **must** be writable in order
to submit batch files.
If you boot to a physical disk device, then the first drive letter (A:)
will be assigned to the disk/slice that you chose to boot from. The A:
drive letter is considered special by most operating systems and is
automatically used in some cases. By making the selected disk/slice the
A: drive, you can setup different disks/slices for specific uses and
just boot to it.
After the first drive letter is assigned (as well as the second drive
letter in the case of a ROM boot), RomWBW will assign additional drive
letters based on the disk drives in the system. Additional drive
letters will be assigned in the following order:
- RAM Disk
- ROM Disk
- Floppy Disk(s)
- Hard Disk(s)
If a disk/slice was already assigned as the A: (or B:) drive letter,
then it will not be assigned again.
In the case of floppy, RAM, and ROM disks, a single drive letter will be
assigned to each physical disk (even if there is no disk media in the
drive).
In the case of hard disks, 1-8 drive letters will be assigned to the
initial 1-8 slices of the disk drive. The number of drive letters
assigned to each hard disk depends on the number of hard disks in the
system:
- 1 Hard Disk: 8 drive letters (slices)
- 2 Hard Disks: 4 drive letters (slices) per disk
- 3+ Hard Disks: 2 drive letters (slices) per disk
This somewhat complicated algorithm is used to try and maximize the
limited number of operating system drive letters available (16) to
the available disk devices as evenly as possible.
Note that for hard disk devices, drive letters will only be assigned
to disk devices that actually contain media. So, for example, if you
have an SD Card slot in your system, but it has no SD Card inserted, then
no drive letters will be assigned to it.
Since drive letter assignments are easily changed at any time using the
`ASSIGN` command, you can customize your assignments as desired after
starting the operating system. Even better, you can use an auto-submit
batch file to customzie the assignments at startup without any user
intervention.
## ROM & RAM Disks
A typical RomWBW system has 512KB of ROM and 512KB of RAM. Some
@@ -2660,6 +2752,166 @@ SAMPLE2.TXT ==> 4:/SAMPLE2.TXT ... [OK]
2 File(s) Copied
```
# Real Time Clock
RomWBW supports a variety of real time clock hardware. If your
system has this hardware, then it will be able to maintain the
current date and time even while your system is turned off.
Additionally, depending on the operating system being used, you may be
able to utilize date/time stamping of files.
You can determine if your system has a real time clock present (and
functioning) by looking at the boot messages. Here is an example of
a boot message reflecting the detection of a valid real time clock
module:
`DSRTC: MODE=STD IO=0x8A Thu 2023-10-19 14:07:11 CHARGE=ON`
This example is from a DSRTC clock module. You may have a different
one, but it will always display the current date/time.
In some cases, your real time clock will support charging of the
battery or super-capacitor while the system has power. The status of
this charging is displayed.
If the date/time of your RTC needs to be updated, you will need to do
this with one of the utilities described below. There is no ability to
update the date/time of the RTC in the RomWBW Boot Loader or Monitor.
## Date/Time Utilities
RomwWBW includes two utilities for displaying or setting the date/time
stored by the RTC. They are both a bit different and are briefly
described below.
### WDATE Utility
The `WDATE` utility (contributed by Kevin Boone) is an application
that will display and/or update the current date/time. Its operation is
described in $doc_apps$. This utility works with any of the supported
RomWBW RTC hardware. Here is an example of displaying and updating the
date/time with this utility:
```
A>wdate
Thursday 19 October 14:14:43 2023
A>wdate 23 10 19 14 24 30
A>wdate
Thursday 19 October 14:24:34 2023
```
Note that `WDATE` does not have anything to do with date/time stamping
of files. It merely displays and sets the real time clock value.
### RTC Utility
Like `WDATE`, the `RTC` utility (contributed by Andrew Lynch) will let
you display and set the current date/time. However, this utility only
works with the DSRTC hardware (DS1302 chip). It is a "direct to
hardware application". Its operation is described in $doc_apps$. Here
is an example of displaying and updatting the date/time with this
utility:
```
A>rtc
Start RTC Program
RomWBW HBIOS, Mark 4 RTC Latch Port 0x8A
RTC: Version 1.9
Commands: E)xit T)ime st(A)rt S)et R)aw L)oop C)harge N)ocharge D)elay I)nit G)et P)ut B)oot W)arm-start H)elp
RTC>t
Current time: 23-10-19 14:30:25-05
RTC>i
Init date/time.
YEAR:23
MONTH:10
DATE:19
HOURS:14
MINUTES:31
SECONDS:00
DAY:05
```
The `RTC` utility is also capable of turning the charging feature of
the DS1320 chip on or off. Here is an example of turning if off and
back on:
```
A>rtc
Start RTC Program
RomWBW HBIOS, Mark 4 RTC Latch Port 0x8A
RTC: Version 1.9
Commands: E)xit T)ime st(A)rt S)et R)aw L)oop C)harge N)ocharge D)elay I)nit G)et P)ut B)oot W)arm-start H)elp
RTC>n
Trickle charger disabled.
RTC>c
Trickle charger enabled.
```
Do **not** enable charging unless you are sure that your system
supports this. If your RTC is being powered by a normal battery, it
would be dangerous to enable charging.
## Date/Time File Stamping
If an RTC is available in your system, then most operating systems
can use it to date/time stamp files. This just means recording the
date/time of file creation, update, and or access in the directory.
This capability is available in all of the RomWBW operating system
except the original DRI CP/M 2.2.
In some cases (such as ZSDOS), you must load an RSX (memory resident
utility) to enable date/time stamping of files. Additionally, you
will need to initialize the directory. The procedure varies in each
operation system, so you must review the associated documentation.
The date/time stamping mechanisms for each operating system are
generally not compatible. If you initialize a directory for a type
of stamping, you should be careful not to manipulate that directory
with a different operating system with a different date/time stamping
mechanism. Doing so may corrupt the directory.
The RomWBW disk images do not have date/time stamping initialized. This
is to avoid any chance of directory corruption.
## Timezone
None of the operating systems distributed with RomWBW have any concept
of timezone. When files are date/time stamped, the date/time will
simply be whatever date/time the RTC currently has.
The normal practice is to set the RTC to your local time. This implies
that you would need to manually adjust the RTC for daylight savings time
and/or when you travel to a different time zone.
The date/time stamps of files in directories will also be stored in
local time. This includes files stored in a FAT filesystem. If you
subsequently view the directory from modern machines (Windows, Linux,
etc.), the date/time displayed will depend on the behavior of the
modern system.
For example, Linux assumes that the date/time of files
is UTC. So, if you create a file on a FAT filesystem with your RomWBW
computer and then use Linux to view the directory, the date/time stamps
will seem "off" by a few hours.
The only alternative you may consider is setting the date/time of your
RTC to UTC. Since UTC is consistent across all timezones and daylight
savings time, your file date/time stamps will also be consistent. Of
course, this will mean that your RomWBW computer will display a
date/time that seems wrong because it is not local time.
# CP/NET Networking
Digital Research created a simple network file sharing system called
@@ -4095,7 +4347,29 @@ the RomWBW HBIOS configuration.
- VGARC Video & Keyboard Module
- Serial baud rate is usually determined by hardware for ACIA and
SIO interfaces
### Z180 Z1RCC CPU Module
| | |
|-------------------|--------------------|
| ROM Image Files | RCZ180_z1rcc.rom |
| Console Baud Rate | 115200 |
| Interrupts | Mode 2 |
- CPU speed is detected at startup if DS1302 RTC is active
- Otherwise 18.432 MHz assumed
- System timer is generated by Z180 CPU
- Hardware auto-detected:
- DS1302 RTC
- Z180 ASCI Serial Ports
- SIO Serial Interface Module
- EP Dual UART Serial Interface Module
- WDC Floppy Disk Controller w/ 3.5" HD Drives
- IDE Hard Disk Interface Module
- PPIDE Hard Disk Interface Module
- Use of Interrupt Mode 2 requires proper IEI/IEO configuration
for all peripherals generating interrupts
`\clearpage`{=latex}
### Z280 ZZRCC CPU Module

View File

@@ -216,6 +216,7 @@ call Build RCZ80 zrc || exit /b
call Build RCZ80 zrc_ram || exit /b
call Build RCZ180 ext || exit /b
call Build RCZ180 nat || exit /b
call Build RCZ180 z1rcc || exit /b
call Build RCZ280 ext || exit /b
call Build RCZ280 nat || exit /b
call Build RCZ280 zz80mb || exit /b

View File

@@ -15,6 +15,7 @@ if [ "${ROM_PLATFORM}" == "dist" ] ; then
ROM_PLATFORM="N8"; ROM_CONFIG="std"; bash Build.sh
ROM_PLATFORM="RCZ180"; ROM_CONFIG="ext"; bash Build.sh
ROM_PLATFORM="RCZ180"; ROM_CONFIG="nat"; bash Build.sh
ROM_PLATFORM="RCZ180"; ROM_CONFIG="z1rcc"; bash Build.sh
ROM_PLATFORM="RCZ280"; ROM_CONFIG="ext"; bash Build.sh
ROM_PLATFORM="RCZ280"; ROM_CONFIG="nat"; bash Build.sh
ROM_PLATFORM="RCZ280"; ROM_CONFIG="zz80mb"; bash Build.sh

View File

@@ -0,0 +1,76 @@
;
;==================================================================================================
; RCBUS Z180 Z1RCC CONFIGURATION (ROMLESS)
;==================================================================================================
;
; THE COMPLETE SET OF DEFAULT CONFIGURATION SETTINGS FOR THIS PLATFORM ARE FOUND IN THE
; CFG_<PLT>.ASM INCLUDED FILE WHICH IS FOUND IN THE PARENT DIRECTORY. THIS FILE CONTAINS
; COMMON CONFIGURATION SETTINGS THAT OVERRIDE THE DEFAULTS. IT IS INTENDED THAT YOU MAKE
; YOUR CUSTOMIZATIONS IN THIS FILE AND JUST INHERIT ALL OTHER SETTINGS FROM THE DEFAULTS.
; EVEN BETTER, YOU CAN MAKE A COPY OF THIS FILE WITH A NAME LIKE <PLT>_XXX.ASM AND SPECIFY
; YOUR FILE IN THE BUILD PROCESS.
;
; THE SETTINGS BELOW ARE THE SETTINGS THAT ARE MOST COMMONLY MODIFIED FOR THIS PLATFORM.
; MANY OF THEM ARE EQUAL TO THE SETTINGS IN THE INCLUDED FILE, SO THEY DON'T REALLY DO
; ANYTHING AS IS. THEY ARE LISTED HERE TO MAKE IT EASY FOR YOU TO ADJUST THE MOST COMMON
; SETTINGS.
;
; N.B., SINCE THE SETTINGS BELOW ARE REDEFINING VALUES ALREADY SET IN THE INCLUDED FILE,
; TASM INSISTS THAT YOU USE THE .SET OPERATOR AND NOT THE .EQU OPERATOR BELOW. ATTEMPTING
; TO REDEFINE A VALUE WITH .EQU BELOW WILL CAUSE TASM ERRORS!
;
; PLEASE REFER TO THE CUSTOM BUILD INSTRUCTIONS (README.TXT) IN THE SOURCE DIRECTORY (TWO
; DIRECTORIES ABOVE THIS ONE).
;
#DEFINE PLATFORM_NAME "Z1RCC", " [", CONFIG, "]"
;
#DEFINE BOOT_DEFAULT "H" ; DEFAULT BOOT LOADER CMD ON <CR> OR AUTO BOOT
;
#include "cfg_rcz180.asm"
;
CPUOSC .SET 18432000 ; CPU OSC FREQ IN MHZ
CRTACT .SET FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
;
FPLED_ENABLE .SET TRUE ; FP: ENABLES FRONT PANEL LEDS
FPSW_ENABLE .SET TRUE ; FP: ENABLES FRONT PANEL SWITCHES
;
MEMMGR .SET MM_Z180 ; MEMORY MANAGER: MM_[SBC|Z2|N8|Z180]
;
RAMSIZE .SET 512 ; SIZE OF RAM IN KB (MUST MATCH YOUR HARDWARE!!!)
ROMSIZE .SET 0 ; SIZE OF ROM IN KB (MUST MATCH YOUR HARDWARE!!!)
;
Z180_CLKDIV .SET 1 ; Z180: CHK DIV: 0=OSC/2, 1=OSC, 2=OSC*2
Z180_MEMWAIT .SET 0 ; Z180: MEMORY WAIT STATES (0-3)
Z180_IOWAIT .SET 1 ; Z180: I/O WAIT STATES TO ADD ABOVE 1 W/S BUILT-IN (0-3)
;
MDROM .SET FALSE ; MD: ENABLE ROM DISK
MDRAM .SET TRUE ; MD: ENABLE RAM DISK
;
DSRTCENABLE .SET TRUE ; DSRTC: ENABLE DS-1302 CLOCK DRIVER (DSRTC.ASM)
INTRTCENABLE .SET TRUE ; ENABLE PERIODIC INTERRUPT CLOCK DRIVER (INTRTC.ASM)
;
UARTENABLE .SET TRUE ; UART: ENABLE 8250/16550-LIKE SERIAL DRIVER (UART.ASM)
ASCIENABLE .SET TRUE ; ASCI: ENABLE Z180 ASCI SERIAL DRIVER (ASCI.ASM)
ACIAENABLE .SET FALSE ; ACIA: ENABLE MOTOROLA 6850 ACIA DRIVER (ACIA.ASM)
SIOENABLE .SET TRUE ; SIO: ENABLE ZILOG SIO SERIAL DRIVER (SIO.ASM)
;
TMSENABLE .SET FALSE ; TMS: ENABLE TMS9918 VIDEO/KBD DRIVER (TMS.ASM)
TMSMODE .SET TMSMODE_MSX ; TMS: DRIVER MODE: TMSMODE_[SCG|N8|MBC|MSX|MSX9958|MSXKBD|COLECO]
MKYENABLE .SET FALSE ; MSX 5255 PPI KEYBOARD COMPATIBLE DRIVER (REQUIRES TMS VDA DRIVER)
VRCENABLE .SET FALSE ; VRC: ENABLE VGARC VIDEO/KBD DRIVER (VRC.ASM)
VDAEMU_SERKBD .SET 0 ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
;
;
AY38910ENABLE .SET FALSE ; AY: AY-3-8910 / YM2149 SOUND DRIVER
AYMODE .SET AYMODE_RCZ180 ; AY: DRIVER MODE: AYMODE_[SCG|N8|RCZ80|RCZ180|MSX|LINC]
SN76489ENABLE .SET FALSE ; SN: ENABLE SN76489 SOUND DRIVER
;
FDENABLE .SET TRUE ; FD: ENABLE FLOPPY DISK DRIVER (FD.ASM)
FDMODE .SET FDMODE_RCWDC ; FD: DRIVER MODE: FDMODE_[DIO|ZETA|ZETA2|DIDE|N8|DIO3|RCSMC|RCWDC|DYNO|EPFDC]
;
IDEENABLE .SET TRUE ; IDE: ENABLE IDE DISK DRIVER (IDE.ASM)
PPIDEENABLE .SET TRUE ; PPIDE: ENABLE PARALLEL PORT IDE DISK DRIVER (PPIDE.ASM)
SDENABLE .SET FALSE ; SD: ENABLE SD CARD DISK DRIVER (SD.ASM)
SDCNT .SET 1 ; SD: NUMBER OF SD CARD DEVICES (1-2), FOR DSD/SC/MT SC ONLY
;
PRPENABLE .SET FALSE ; PRP: ENABLE ECB PROPELLER IO BOARD DRIVER (PRP.ASM)

View File

@@ -178,10 +178,26 @@ ACIA1_INT:
;
ACIA_INTRCV:
; CHECK TO SEE IF SOMETHING IS ACTUALLY THERE
CALL DELAY
LD C,(IY+3) ; CMD/STAT PORT TO C
IN A,(C) ; GET STATUS
RRA ; READY BIT TO CF
RET NC ; NOTHING AVAILABLE ON CURRENT CHANNEL
LD B,A
AND $01 ; ISOLATE READY BIT
JR NZ,ACIA_INTRCV1
;
#IF FALSE
CALL PC_LT
LD A,B
CALL PRTHEXBYTE
INC C
IN A,(C)
CALL PRTHEXBYTE
CALL PC_GT
OR $FF
#ENDIF
;
RET
;
ACIA_INTRCV1:
; RECEIVE CHARACTER INTO BUFFER

View File

@@ -88,6 +88,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -128,6 +129,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU TRUE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU TRUE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART
@@ -229,14 +231,14 @@ SDCNT .EQU 1 ; SD: NUMBER OF SD CARD DEVICES (1-2), FOR DSD/SC/MT ONLY
SDTRACE .EQU 1 ; SD: TRACE LEVEL (0=NO,1=ERRORS,2=ALL)
SDCSIOFAST .EQU FALSE ; SD: ENABLE TABLE-DRIVEN BIT INVERTER IN CSIO MODE
;
CHENABLE .EQU FALSE ; CH: ENABLE CH375/376 USB SUPPORT
CHENABLE .EQU TRUE ; CH: ENABLE CH375/376 USB SUPPORT
CHTRACE .EQU 1 ; CH: TRACE LEVEL (0=NO,1=ERRORS,2=ALL)
CHUSBTRACE .EQU 1 ; CHUSB: TRACE LEVEL (0=NO,1=ERRORS,2=ALL)
CHSDTRACE .EQU 1 ; CHSD: TRACE LEVEL (0=NO,1=ERRORS,2=ALL)
CHCNT .EQU 1 ; CH: NUMBER OF BOARDS TO DETECT (1-2)
CH0BASE .EQU $BE ; CH 0: BASE I/O ADDRESS
CH0BASE .EQU $4E ; CH 0: BASE I/O ADDRESS
CH0USBENABLE .EQU TRUE ; CH 0: ENABLE USB DISK
CH0SDENABLE .EQU FALSE ; CH 0: ENABLE SD DISK
CH0SDENABLE .EQU TRUE ; CH 0: ENABLE SD DISK
CH1BASE .EQU $FF ; CH 1: BASE I/O ADDRESS
CH1USBENABLE .EQU FALSE ; CH 1: ENABLE USB DISK
CH1SDENABLE .EQU FALSE ; CH 1: ENABLE SD DISK
@@ -254,16 +256,16 @@ ESPCONENABLE .EQU TRUE ; ESP: ENABLE ESP32 CONSOLE IO DRIVER VIDEO/KBD SUPPORT
HDSKENABLE .EQU FALSE ; HDSK: ENABLE SIMH HDSK DISK DRIVER (HDSK.ASM)
HDSKTRACE .EQU 1 ; HDSK: TRACE LEVEL (0=NO,1=ERRORS,2=ALL)
;
PIOENABLE .EQU FALSE ; PIO: ENABLE ZILOG PIO DRIVER (PIO.ASM)
PIOENABLE .EQU TRUE ; PIO: ENABLE ZILOG PIO DRIVER (PIO.ASM)
PIOCNT .EQU 2 ; PIO: NUMBER OF CHIPS TO DETECT (1-2), 2 CHANNELS PER CHIP
PIO0BASE .EQU $B8 ; PIO 0: REGISTERS BASE ADR
PIO1BASE .EQU $BC ; PIO 1: REGISTERS BASE ADR
PIO0BASE .EQU $68 ; PIO 0: REGISTERS BASE ADR
PIO1BASE .EQU $6C ; PIO 1: REGISTERS BASE ADR
;
LPTENABLE .EQU FALSE ; LPT: ENABLE CENTRONICS PRINTER DRIVER (LPT.ASM)
LPTENABLE .EQU TRUE ; LPT: ENABLE CENTRONICS PRINTER DRIVER (LPT.ASM)
LPTMODE .EQU LPTMODE_SPP ; LPT: DRIVER MODE: LPTMODE_[NONE|SPP|MG014]
LPTCNT .EQU 1 ; LPT: NUMBER OF CHIPS TO DETECT (1-2)
LPTTRACE .EQU 1 ; LPT: TRACE LEVEL (0=NO,1=ERRORS,2=ALL)
LPT0BASE .EQU $E8 ; LPT 0: REGISTERS BASE ADR
LPT0BASE .EQU $48 ; LPT 0: REGISTERS BASE ADR
LPT1BASE .EQU $EC ; LPT 1: REGISTERS BASE ADR
;
PPAENABLE .EQU FALSE ; PPA: ENABLE PPA DISK DRIVER (PPA.ASM)

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -136,6 +137,7 @@ UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG | SER_RTS ; UART: LINE CONFIG FOR UART PORTS
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -136,6 +137,7 @@ UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG | SER_RTS ; UART: LINE CONFIG FOR UART PORTS
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -119,6 +119,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -166,6 +167,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -85,6 +85,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -125,6 +126,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU TRUE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -130,6 +131,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU TRUE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU TRUE ; UART: AUTO-DETECT 4UART UART

View File

@@ -92,6 +92,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -132,6 +133,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU TRUE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU TRUE ; UART: AUTO-DETECT 4UART UART

View File

@@ -96,6 +96,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -142,6 +143,7 @@ UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG | SER_RTS ; UART: LINE CONFIG FOR UART PORTS
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -136,6 +137,7 @@ UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG | SER_RTS ; UART: LINE CONFIG FOR UART PORTS
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -136,6 +137,7 @@ UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG | SER_RTS ; UART: LINE CONFIG FOR UART PORTS
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -130,6 +131,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -136,6 +137,7 @@ UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG | SER_RTS ; UART: LINE CONFIG FOR UART PORTS
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -85,6 +85,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -125,6 +126,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU TRUE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU TRUE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU TRUE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU TRUE ; UART: AUTO-DETECT 4UART UART

View File

@@ -90,6 +90,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -135,6 +136,7 @@ UARTOSC .EQU 1843200 ; UART: OSC FREQUENCY IN MHZ
UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG | SER_RTS ; UART: LINE CONFIG FOR UART PORTS
UARTSBC .EQU FALSE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART

View File

@@ -88,6 +88,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -122,6 +123,7 @@ UARTINTS .EQU FALSE ; UART: INCLUDE INTERRUPT SUPPORT UNDER IM1/2/3
UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU TRUE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART

View File

@@ -77,6 +77,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -112,6 +113,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU TRUE ; UART: AUTO-DETECT SBC/ZETA ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -88,6 +88,7 @@ PKDOSC .EQU 3000000 ; OSCILLATOR FREQ FOR PKD (IN HZ)
H8PENABLE .EQU FALSE ; ENABLES HEATH H8 FRONT PANEL
;
BOOTCON .EQU 0 ; BOOT CONSOLE DEVICE
SECCON .EQU $FF ; SECONDARY CONSOLE DEVICE
CRTACT .EQU FALSE ; ACTIVATE CRT (VDU,CVDU,PROPIO,ETC) AT STARTUP
VDAEMU .EQU EMUTYP_ANSI ; VDA EMULATION: EMUTYP_[TTY|ANSI]
VDAEMU_SERKBD .EQU $FF ; VDA EMULATION: SERIAL KBD UNIT #, OR $FF FOR HW KBD
@@ -123,6 +124,7 @@ UARTCFG .EQU DEFSERCFG ; UART: LINE CONFIG FOR UART PORTS
UARTCASSPD .EQU SER_300_8N1 ; UART: ECB CASSETTE UART DEFAULT SPEED
UARTSBC .EQU TRUE ; UART: AUTO-DETECT SBC/ZETA/DUO ONBOARD UART
UARTSBCFORCE .EQU FALSE ; UART: FORCE DETECTION OF SBC UART (FOR SIMH)
UARTAUX .EQU FALSE ; UART: AUTO-DETECT AUX UART
UARTCAS .EQU FALSE ; UART: AUTO-DETECT ECB CASSETTE UART
UARTMFP .EQU FALSE ; UART: AUTO-DETECT MF/PIC UART
UART4 .EQU FALSE ; UART: AUTO-DETECT 4UART UART

View File

@@ -18,9 +18,8 @@
; added to the RESET routine when using CH376.
;
; TODO:
; - Implement auto-recovery on error status.
;
CHUSB_FASTIO .EQU TRUE ; USE INIR/OTIR?
; - Implement auto-recovery on error status?
; - !!! Move CH_MODE to config variable !!!
;
; PORT OFFSETS FROM BASE PORT
;
@@ -33,6 +32,12 @@ CHTYP_NONE .EQU 0 ; NONE
CHTYP_375 .EQU 1 ; CH375
CHTYP_376 .EQU 2 ; CH376
;
; CH MODE MANAGEMENT
;
CH_MODE_UNK .EQU 0 ; CURRENT MODE UNKNOWN
CH_MODE_USB .EQU 1 ; CURRENT MODE = USB
CH_MODE_SD .EQU 2 ; CURRENT MODE = SD
;
; CH375/376 COMMANDS
;
CH_CMD_VER .EQU $01 ; GET IC VER
@@ -41,6 +46,7 @@ CH_CMD_EXIST .EQU $06 ; CHECK EXISTS
CH_CMD_MAXLUN .EQU $0A ; GET MAX LUN NUMBER
CH_CMD_PKTSEC .EQU $0B ; SET PACKETS PER SECTOR
CH_CMD_SETRETRY .EQU $0B ; SET RETRIES
CH_CMD_FILESIZE .EQU $0C ; GET FILE SIZE (376)
CH_CMD_MODE .EQU $15 ; SET USB MODE
CH_CMD_TSTCON .EQU $16 ; TEST CONNECT
CH_CMD_ABRTNAK .EQU $17 ; ABORT DEVICE NAK RETRIES
@@ -49,7 +55,16 @@ CH_CMD_RD6 .EQU $27 ; READ USB DATA (375 & 376)
CH_CMD_RD5 .EQU $28 ; READ USB DATA (375)
CH_CMD_WR5 .EQU $2B ; WRITE USB DATA (375)
CH_CMD_WR6 .EQU $2C ; WRITE USB DATA (376)
CH_CMD_WRREQDAT .EQU $2D ; WRITE REQUESTED DATA (376)
CH_CMD_SET_FN .EQU $2F ; SET FILENAME (376)
CH_CMD_DSKMNT .EQU $31 ; DISK MOUNT
CH_CMD_FOPEN .EQU $32 ; FILE OPEN (376)
CH_CMD_FCREAT .EQU $34 ; FILE CREATE (376)
CH_CMD_BYTE_LOC .EQU $39 ; BYTE LOCATE
CH_CMD_BYTERD .EQU $3A ; BYTE READ
CH_CMD_BYTERDGO .EQU $3B ; BYTE READ GO
CH_CMD_BYTEWR .EQU $3C ; BYTE WRITE
CH_CMD_BYTEWRGO .EQU $3D ; BYTE WRITE GO
CH_CMD_DSKCAP .EQU $3E ; DISK CAPACITY
CH_CMD_AUTOSET .EQU $4D ; USB AUTO SETUP
CH_CMD_DSKINIT .EQU $51 ; DISK INIT
@@ -129,6 +144,8 @@ CH_INIT2:
LD A,(IY+CH_IOBASE) ; GET IO BASE ADDRES
CALL PRTHEXBYTE ; DISPLAY IT
;
XOR A ; UNKNOWN MODE
LD (CH_MODE),A ; SAVE IT
;CALL CH_FLUSH ; FLUSH DEVICE OUTPUT QUEUE
CALL CH_RESET ; FULL CH37X RESET
CALL CH_DETECT ; DETECT CHIP PRESENCE
@@ -347,18 +364,63 @@ CH_GETVER:
CALL CH_RD ; GET VERSION BYTE
RET ; DONE
;
; SET MODE TO VALUE IN A
; AVOID CHANGING MODES IF CURRENT MODE = NEW MODE
; THE CH376 DOES NOT SEEM TO MAINTAIN SEPARATE OPERATING CONTEXTS FOR
; THE USB AND SD DEVICES. IF BOTH ARE IN OPERATION, THEN A MODE
; SWITCH REQUIRES A COMPLETE REINITIALIZATION OF THE REQUESTED
; DEVICE.
;
CH_SETMODE:
PUSH BC ; SAVE BC
PUSH DE ; SAVE DE
PUSH HL ; SAVE HL
;PRTS("\r\nSETMODE:$") ; *DEBUG*
LD L,A ; SAVE REQUESTED MODE
LD A,(CH_MODE) ; GET CURRENT MODE
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP L ; COMPARE
JR Z,CH_SETMODE_Z ; IF EQUAL, DONE
;
; NEED TO CHANGE MODES
LD A,L ; GET REQUESTED MODE
CP CH_MODE_USB ; USB?
JR Z,CH_SETMODE_USB ; IF SO, DO IT
CP CH_MODE_SD ; SD?
JR Z,CH_SETMODE_SD ; IF SO, DO IT
OR $FF ; SIGNAL ERROR
JR CH_SETMODE_Z ; BAIL OUT
;
CH_SETMODE_USB:
CALL CHUSB_RESET ; FULL USB STACK RESET
JR CH_SETMODE_Z ; MOVE ON
;
CH_SETMODE_SD:
CALL CHSD_RESET ; FULL SD STACK RESET
JR CH_SETMODE_Z ; MOVE ON
;
CH_SETMODE_Z:
POP HL ; RECOVER HL
POP DE ; RECOVER DE
POP BC ; RECOVER BC
RET ; DONE
;
;
CH_STR_NOHW .TEXT " NOT PRESENT$"
CH_STR_UPGRADE .TEXT " !!!UPGRADE REQUIRED!!!$"
;
CH_STR_375 .TEXT "CH375$"
CH_STR_376 .TEXT "CH376$"
CH_MODE .DB CH_MODE_UNK
;
CH_STR_NOHW .TEXT " NOT PRESENT$"
;
CH_STR_375 .TEXT "CH375$"
CH_STR_376 .TEXT "CH376$"
;
;==================================================================================================
; CH375/376 USB SUB-DRIVER
;==================================================================================================
;
CHUSB_FASTIO .EQU TRUE ; USE INIR/OTIR?
;
; CHUSB DEVICE STATUS
;
CHUSB_STOK .EQU 0
@@ -368,7 +430,7 @@ CHUSB_STIOERR .EQU -3
CHUSB_STTO .EQU -4
CHUSB_STNOTSUP .EQU -5
;
; CH DEVICE CONFIGURATION
; CHUSB DEVICE CONFIGURATION
;
CHUSB_CFGSIZ .EQU 12 ; SIZE OF USB CFG TBL ENTRIES
;
@@ -481,6 +543,10 @@ CHUSB_DEFMED:
;
;
CHUSB_READ:
LD A,CH_MODE_USB ; REQUEST USB MODE
CALL CH_SETMODE ; DO IT
JP NZ,CHUSB_CMDERR ; HANDLE ERROR
;
CALL HB_DSKREAD ; HOOK HBIOS DISK READ SUPERVISOR
LD (CHUSB_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
LD A,CH_CMD_DSKRD ; DISK READ COMMAND
@@ -542,6 +608,10 @@ CHUSB_READ2:
;
;
CHUSB_WRITE:
LD A,CH_MODE_USB ; REQUEST USB MODE
CALL CH_SETMODE ; DO IT
JP NZ,CHUSB_CMDERR ; HANDLE ERROR
;
CALL HB_DSKWRITE ; HOOK HBIOS DISK WRITE SUPERVISOR
LD (CHUSB_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
LD A,CH_CMD_DSKWR ; DISK READ COMMAND
@@ -631,7 +701,7 @@ CHUSB_STATUS:
; RESET THE INTERFACE AND REDISCOVER MEDIA
;
CHUSB_RESET:
;PRTS("\n\rRESET:$") ; *DEBUG*
;PRTS("\n\rRES USB:$") ; *DEBUG*
;CALL CH_FLUSH ; DISCARD ANY GARBAGE
;CALL CH_RESET ; FULL CH37X RESET
;
@@ -656,6 +726,9 @@ CHUSB_RESET:
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CALL CH_NAP ; SMALL WAIT
;
LD A,CH_MODE_USB ; WE ARE NOW IN USB MODE
LD (CH_MODE),A ; SAVE IT
;
; INITIALIZE DISK
LD B,24 ; TRY A FEW TIMES
@@ -671,29 +744,28 @@ CHUSB_RESET1:
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $14 ; SUCCESS?
JR Z,CHUSB_RESET2 ; IF SO, CONTINUE
;JR Z,CHUSB_RESET1A ; IF SO, CHECK READY
JR Z,CHUSB_RESET1A ; IF SO, CHECK READY
CP $16 ; NO MEDIA
JP Z,CHUSB_NOMEDIA ; HANDLE IT
CALL CH_NAP ; SMALL DELAY
DJNZ CHUSB_RESET1 ; LOOP AS NEEDED
JP CHUSB_TO ; HANDLE TIMEOUT
;;;;
;;;CHUSB_RESET1A:
;;; CALL CHUSB_DSKRES ; DISK RESET
;;; CP $14 ; GOOD?
;;; JR Z,CHUSB_RESET2
;;; CALL CHUSB_DSKRDY ; CHECK IF DISK READY
;;; CP $14 ; GOOD?
;;; JR Z,CHUSB_RESET2 ; IF SO, MOVE ON
;;; DJNZ CHUSB_RESET1 ; KEEP TRYING
;
CHUSB_RESET1A:
;CALL CHUSB_DSKRES ; DISK RESET
;CP $14 ; GOOD?
;JR Z,CHUSB_RESET2
;CALL CHUSB_DSKRDY ; CHECK IF DISK READY
;CP $14 ; GOOD?
;JR Z,CHUSB_RESET2 ; IF SO, MOVE ON
;DJNZ CHUSB_RESET1 ; KEEP TRYING
;
CHUSB_RESET2:
; USE OF CH376 DISK_MOUNT COMMAND SEEMS TO IMPROVE
; COMPATIBILITY WITH SOME OLDER USB THUMBDRIVES.
LD A,(IY+CH_TYPE) ; CH37X TYPE?
CP CHTYP_376 ; IS CH376?
CALL Z,CHUSB_DSKMNT ; IF SO, ISSUE MOUNT
CALL Z,CHUSB_DSKMNT ; IF SO, TRY MOUNT, IGNORE ERRS
;CALL CHUSB_AUTOSET ; *DEBUG*
;CALL CHUSB_TSTCON ; *DEBUG*
;CALL CHUSB_MAXLUN ; *DEBUG*
@@ -714,7 +786,7 @@ CHUSB_RESET2:
CHUSB_DEVICE:
LD D,DIODEV_CHUSB ; D := DEVICE TYPE
LD E,(IY+CH_DEV) ; E := PHYSICAL DEVICE NUMBER
LD C,%01110011 ; USB HARD DISK ATTRIBUTES
LD C,%00110011 ; USB HARD DISK ATTRIBUTES
LD H,(IY+CH_TYPE) ; H := MODE
LD L,(IY+CH_IOBASE) ; L := BASE I/O ADDRESS
XOR A ; SIGNAL SUCCESS
@@ -791,28 +863,34 @@ CHUSB_DSKMNT:
;CALL PRTHEXBYTE ; *DEBUG*
CP $14 ; SUCCESS?
RET NZ ; ABORT IF NOT
;
#IF FALSE
CALL CH_CMD_RD ; SEND READ COMMAND
CALL CH_RD ; GET LENGTH
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
LD B,A ; LOOP COUNTER
LD HL,HB_WRKBUF ; USE WORK BUFFER FOR DATA
DSKMNT1:
CHUSB_DSKMNT1:
CALL CH_RD ; GET A BYTE
LD (HL),A ; SAVE IT
INC HL ; BUMP BUF PTR
DJNZ DSKMNT1 ; LOOP FOR ALL DATA
DJNZ CHUSB_DSKMNT1 ; LOOP FOR ALL DATA
;
;LD DE,HB_WRKBUF ; *DEBUG*
;CALL DUMP_BUFFER ; *DEBUG*
;
;CALL CHUSB_PRTPREFIX ; PRINT DEVICE PREFIX
;LD HL,HB_WRKBUF + 8
;LD B,28
DSKMNT2:
;LD A,(HL)
;INC HL
;CALL COUT
;DJNZ DSKMNT2
CALL CHUSB_PRTPREFIX ; PRINT DEVICE PREFIX
LD HL,HB_WRKBUF + 8
LD B,28
CHUSB_DSKMNT2:
LD A,(HL)
INC HL
CALL COUT
DJNZ CHUSB_DSKMNT2
#ENDIF
;
XOR A
RET
;
; PERFORM DISK SIZE
@@ -1081,17 +1159,30 @@ CHUSB_STR_ST_MAP:
CHUSB_STR_STOK .TEXT "OK$"
CHUSB_STR_STNOMEDIA .TEXT "NO MEDIA$"
CHUSB_STR_STCMDERR .TEXT "COMMAND ERROR$"
CHUSB_STR_STIOERR .TEXT "IO ERROR$"
CHUSB_STR_STIOERR .TEXT "IO ERROR$"
CHUSB_STR_STTO .TEXT "TIMEOUT$"
CHUSB_STR_STNOTSUP .TEXT "NOT SUPPORTED$"
CHUSB_STR_STUNK .TEXT "UNKNOWN ERROR$"
;
;==================================================================================================
; CH375/376 SD CARD SUB-DRIVER
;==================================================================================================
;
; CH DEVICE CONFIGURATION
#DEFINE CHSD_IMGFILE "DISK.IMG"
;
CHSD_FASTIO .EQU TRUE ; USE INIR/OTIR?
;
; CHUSB DEVICE STATUS
;
CHSD_STOK .EQU 0
CHSD_STNOMEDIA .EQU -1
CHSD_STCMDERR .EQU -2
CHSD_STIOERR .EQU -3
CHSD_STTO .EQU -4
CHSD_STNOTSUP .EQU -5
CHSD_STNOFILE .EQU -6
;
; CHSD DEVICE CONFIGURATION
;
CHSD_CFGSIZ .EQU 12 ; SIZE OF USB CFG TBL ENTRIES
;
@@ -1144,36 +1235,581 @@ CHSD_INIT:
INC A ; BUMP TO NEXT UNIT NUM TO ASSIGN
LD (CHSD_DEVNUM),A ; SAVE IT
;
CALL NEWLINE ; FORMATTING
PRTS("CHSD$") ; LABEL FOR IO ADDRESS
LD A,(IY+CH_DEV) ; GET DEVICE NUM
CALL PRTDECB ; PRINT IT
CALL PC_COLON ; FORMATTING
; ADD UNIT TO GLOBAL DISK UNIT TABLE
LD BC,CHSD_FNTBL ; BC := FUNC TABLE ADR
PUSH IY ; CFG ENTRY POINTER
POP DE ; COPY TO DE
CALL DIO_ADDENT ; ADD ENTRY TO GLOBAL DISK DEV TABLE
;
CALL CHSD_RESET ; RESET & DISCOVER MEDIA
#IF (CHSDTRACE <= 1)
CALL NZ,CHSD_PRTSTAT
#ENDIF
RET NZ ; ABORT ON FAILURE
;
; START PRINTING DEVICE INFO
CALL CHSD_PRTPREFIX ; PRINT DEVICE PREFIX
;
; PRINT STORAGE CAPACITY (BLOCK COUNT)
PRTS(" BLOCKS=0x$") ; PRINT FIELD LABEL
LD A,CHSD_MEDCAP ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; GET THE CAPACITY VALUE
CALL PRTHEX32 ; PRINT HEX VALUE
;
; PRINT STORAGE SIZE IN MB
PRTS(" SIZE=$") ; PRINT FIELD LABEL
LD B,11 ; 11 BIT SHIFT TO CONVERT BLOCKS --> MB
CALL SRL32 ; RIGHT SHIFT
CALL PRTDEC32 ; PRINT DWORD IN DECIMAL
PRTS("MB$") ; PRINT SUFFIX
;
XOR A ; SIGNAL SUCCESS
RET
;
; DRIVER FUNCTION TABLE
;
CHSD_FNTBL:
.DW CHSD_STATUS
.DW CHSD_RESET
.DW CHSD_SEEK
.DW CHSD_READ
.DW CHSD_WRITE
.DW CHSD_VERIFY
.DW CHSD_FORMAT
.DW CHSD_DEVICE
.DW CHSD_MEDIA
.DW CHSD_DEFMED
.DW CHSD_CAP
.DW CHSD_GEOM
#IF (($ - CHSD_FNTBL) != (DIO_FNCNT * 2))
.ECHO "*** INVALID CHSD FUNCTION TABLE ***\n"
#ENDIF
;
CHSD_VERIFY:
CHSD_FORMAT:
CHSD_DEFMED:
SYSCHKERR(ERR_NOTIMPL) ; NOT IMPLEMENTED
RET
;
;
;
CHSD_READ:
LD A,CH_MODE_SD ; REQUEST SD MODE
CALL CH_SETMODE ; DO IT
JP NZ,CHSD_CMDERR ; HANDLE ERROR
;
CALL HB_DSKREAD ; HOOK HBIOS DISK READ SUPERVISOR
LD (CHSD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
CALL CHSD_RWSTART ; SET LBA OFFSET
RET NZ
;
;PRTS("\n\rREAD:$") ; *DEBUG*
LD A,CH_CMD_BYTERD ; BYTE READ
CALL CH_CMD ; SEND COMMAND
CALL CH_NAP
LD A,0 ; LSB
CALL CH_WR ; SEND IT
LD A,2 ; MSB
CALL CH_WR ; SEND IT
CALL CH_POLL ; GET RESULT
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $1D ; DATA READY TO READ?
JP NZ,CHSD_IOERR ; HANDLE I/O ERROR
;
LD HL,(CHSD_DSKBUF)
CHSD_READ1:
CALL CH_CMD_RD ; SEND READ USB DATA CMD
CALL CH_RD ; GET DATA LENGTH
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
;
#IF (CHSD_FASTIO)
LD B,A ; BYTE COUNT TO READ
LD C,(IY+CH_IOBASE) ; BASE PORT
INIR ; DO IT FAST
#ELSE
LD B,A ; SAVE IT
CHSD_READ2:
CALL CH_RD ; GET DATA BYTE
LD (HL),A ; SAVE IN BUFFER
INC HL ; INC BUF PTR
DJNZ CHSD_READ2 ; LOOP TILL DONE W/ ALL BYTES
#ENDIF
;
LD A,CH_CMD_BYTERDGO ; BYTE READ GO COMMAND
CALL CH_CMD ; SEND IT
CALL CH_NAP
CALL CH_POLL ; GET RESULT
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $1D ; MORE?
JR Z,CHSD_READ1 ; IF SO, GET MORE
CP $14 ; GOOD FINISH?
JP NZ,CHSD_IOERR ; HANDLE ERROR
;
; INCREMENT LBA
PUSH HL ; SAVE HL
LD A,CHSD_LBA ; LBA OFFSET
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL INC32HL ; INCREMENT THE VALUE
POP HL ; RESTORE HL
;
XOR A ; SIGNAL SUCCESS
RET
;
;
;
CHSD_WRITE:
LD A,CH_MODE_SD ; REQUEST SD MODE
CALL CH_SETMODE ; DO IT
JP NZ,CHSD_CMDERR ; HANDLE ERROR
;
CALL HB_DSKWRITE ; HOOK HBIOS DISK WRITE SUPERVISOR
LD (CHSD_DSKBUF),HL ; SAVE DISK BUFFER ADDRESS
CALL CHSD_RWSTART ; SET LBA OFFSET'
RET NZ
;
;PRTS("\n\rWRITE:$") ; *DEBUG*
LD A,CH_CMD_BYTEWR ; BYTE WRITE
CALL CH_CMD ; SEND COMMAND
LD A,0 ; LSB
CALL CH_WR ; SEND IT
LD A,2 ; MSB
CALL CH_WR ; SEND IT
CALL CH_POLL ; GET RESULT
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $1E ; DATA READY TO GO?
JP NZ,CHSD_IOERR ; HANDLE I/O ERROR
;
LD HL,(CHSD_DSKBUF)
CHSD_WRITE1:
LD A,CH_CMD_WRREQDAT ; WRITE REQUESTED DATA CMD
CALL CH_CMD ; SEND IT
CALL CH_RD ; GET DATA LENGTH
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
;
#IF (CHSD_FASTIO)
LD B,A ; BYTE COUNT TO WRITE
LD C,(IY+CH_IOBASE) ; BASE PORT
OTIR ; DO IT FAST
#ELSE
LD B,A ; SAVE IT
CHSD_WRITE2:
CALL CH_WR ; WRITE DATA BYTE
LD (HL),A ; SAVE IN BUFFER
INC HL ; INC BUF PTR
DJNZ CHSD_WRITE2 ; LOOP TILL DONE W/ ALL BYTES
#ENDIF
;
LD A,CH_CMD_BYTEWRGO ; BYTE WRITE GO COMMAND
CALL CH_CMD ; SEND IT
CALL CH_NAP
CALL CH_POLL ; GET RESULT
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $1E ; MORE?
JR Z,CHSD_WRITE1 ; IF SO, SEND MORE
CP $14 ; GOOD FINISH?
JP NZ,CHSD_IOERR ; HANDLE ERROR
;
; INCREMENT LBA
PUSH HL ; SAVE HL
LD A,CHSD_LBA ; LBA OFFSET
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL INC32HL ; INCREMENT THE VALUE
POP HL ; RESTORE HL
;
XOR A ; SIGNAL SUCCESS
RET
;
; SEEK TO CURRENT LBA
;
CHSD_RWSTART:
;PRTS("\n\rRWST:$") ; *DEBUG*
LD A,CH_CMD_BYTE_LOC ; BYTE LOCATE COMMAND (SEEK)
CALL CH_CMD ; SEND IT
;
; GET CURRENT LBA OFFSET
LD A,CHSD_LBA ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; OFFSET = DE:HL
;
; CONVERT OFFSET FROM LBA TO BYTE
LD B,9
CHSD_RWSTART1:
SLA L
RL H
RL E
RL D
DJNZ CHSD_RWSTART1
;CALL PRTHEX32 ; *DEBUG*
;
; SEND THE BYTE OFFSET (LSB FIRST)
LD A,L
CALL CH_WR
LD A,H
CALL CH_WR
LD A,E
CALL CH_WR
LD A,D
CALL CH_WR
;
CALL CH_POLL ; WAIT FOR RESPONSE
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $14 ; CHECK RESULT
JP NZ,CHUSB_CMDERR ; HANDLE CMD ERROR
;
XOR A
RET
;
;
;
CHSD_STATUS:
; RETURN UNIT STATUS
LD A,(IY+CHSD_STAT) ; GET STATUS OF SELECTED DEVICE
OR A ; SET FLAGS
RET ; AND RETURN
;
; RESET THE INTERFACE AND REDISCOVER MEDIA
;
CHSD_RESET:
;PRTS("\n\rRES SD:$") ; *DEBUG*
;
; ACTIVATE SD MODE
LD A,CH_CMD_MODE ; SET MODE COMMAND
CALL CH_CMD ; SEND IT
LD A,3 ; SD MODE
CALL CH_WR ; SEND IT
CALL CH_NAP ; SMALL WAIT
CALL CH_RD ; GET RESULT
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CALL CH_NAP ; SMALL WAIT
;
LD A,CH_MODE_SD ; WE ARE NOW IN SD MODE
LD (CH_MODE),A ; SAVE IT
;
CALL CHSD_DSKMNT ; MOUNT DISK
RET NZ
;
; OPEN DISK IMAGE FILE
LD DE,CHSD_FNAME
CALL CHSD_FOPEN
RET NZ
;
; GET FILESIZE
CALL CHSD_FILESIZE
RET NZ
;
; SET STATUS AND RETURN
XOR A ; CLEAR STATUS
LD (IY+CHSD_STAT),A ; RECORD STATUS
OR A ; SET FLAGS
RET ; AND RETURN
;
;
;
CHSD_DEVICE:
LD D,DIODEV_CHSD ; D := DEVICE TYPE
LD E,(IY+CH_DEV) ; E := PHYSICAL DEVICE NUMBER
LD C,%00110010 ; SD HARD DISK ATTRIBUTES
LD H,(IY+CH_TYPE) ; H := MODE
LD L,(IY+CH_IOBASE) ; L := BASE I/O ADDRESS
XOR A ; SIGNAL SUCCESS
RET
;
; CHSD_GETMED
;
CHSD_MEDIA:
LD A,E ; GET FLAGS
OR A ; SET FLAGS
JR Z,CHSD_MEDIA1 ; JUST REPORT CURRENT STATUS AND MEDIA
CALL CHSD_RESET ; RESET CHSD INTERFACE
;
CHSD_MEDIA1:
LD A,(IY+CHSD_STAT) ; GET STATUS
OR A ; SET FLAGS
LD D,0 ; NO MEDIA CHANGE DETECTED
LD E,MID_HD ; ASSUME WE ARE OK
RET Z ; RETURN IF GOOD INIT
LD E,MID_NONE ; SIGNAL NO MEDIA
LD A,ERR_NOMEDIA ; NO MEDIA ERROR
OR A ; SET FLAGS
RET ; AND RETURN
;
;
;
CHSD_SEEK:
BIT 7,D ; CHECK FOR LBA FLAG
CALL Z,HB_CHS2LBA ; CLEAR MEANS CHS, CONVERT TO LBA
RES 7,D ; CLEAR FLAG REGARDLESS (DOES NO HARM IF ALREADY LBA)
LD (IY+CHSD_LBA+0),L ; SAVE NEW LBA
LD (IY+CHSD_LBA+1),H ; ...
LD (IY+CHSD_LBA+2),E ; ...
LD (IY+CHSD_LBA+3),D ; ...
XOR A ; SIGNAL SUCCESS
RET ; AND RETURN
;
;
;
CHSD_CAP:
LD A,(IY+CHSD_STAT) ; GET STATUS
PUSH AF ; SAVE IT
LD A,CHSD_MEDCAP ; OFFSET TO CAPACITY FIELD
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
CALL LD32 ; GET THE CURRENT CAPACITY INTO DE:HL
LD BC,512 ; 512 BYTES PER BLOCK
POP AF ; RECOVER STATUS
OR A ; SET FLAGS
RET
;
;
;
CHSD_GEOM:
; FOR LBA, WE SIMULATE CHS ACCESS USING 16 HEADS AND 16 SECTORS
; RETURN HS:CC -> DE:HL, SET HIGH BIT OF D TO INDICATE LBA CAPABLE
CALL CHSD_CAP ; GET TOTAL BLOCKS IN DE:HL, BLOCK SIZE TO BC
LD L,H ; DIVIDE BY 256 FOR # TRACKS
LD H,E ; ... HIGH BYTE DISCARDED, RESULT IN HL
LD D,16 | $80 ; HEADS / CYL = 16, SET LBA CAPABILITY BIT
LD E,16 ; SECTORS / TRACK = 16
RET ; DONE, A STILL HAS CHSD_CAP STATUS
;
; CH37X HELPER ROUTINES
;
;
; PERFORM DISK MOUNT
;
CHSD_DSKMNT:
;PRTS("\n\rMOUNT:$") ; *DEBUG*
LD A,CH_CMD_DSKMNT ; DISK QUERY
CALL CH_CMD ; DO IT
CALL CH_POLL ; WAIT FOR RESPONSE
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $82 ; NO DISK?
JP Z,CHSD_NOMEDIA ; HANDLE NO MEDIA ERROR
CP $14 ; SUCCESS?
JP NZ,CHSD_CMDERR ; HANDLE ERROR
;
#IF FALSE
CALL CH_CMD_RD ; SEND READ COMMAND
CALL CH_RD ; GET LENGTH
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
LD B,A ; LOOP COUNTER
LD HL,HB_WRKBUF ; USE WORK BUFFER FOR DATA
CHSD_DSKMNT1:
CALL CH_RD ; GET A BYTE
LD (HL),A ; SAVE IT
INC HL ; BUMP BUF PTR
DJNZ CHSD_DSKMNT1 ; LOOP FOR ALL DATA
;
;LD DE,HB_WRKBUF ; *DEBUG*
;CALL DUMP_BUFFER ; *DEBUG*
;
CALL CHSD_PRTPREFIX ; PRINT DEVICE PREFIX
LD HL,HB_WRKBUF + 8
LD B,28
CHSD_DSKMNT2:
LD A,(HL)
INC HL
CALL COUT
DJNZ CHSD_DSKMNT2
#ENDIF
;
XOR A
RET
;
; SET FILE NAME
;
CHSD_SETFNAME:
;PRTS("\n\rSETFNAME:$") ; *DEBUG*
LD A,CH_CMD_SET_FN ; SET FILE NAME COMMAND
CALL CH_CMD ; SEND IT
CALL CH_NAP
;CALL DELAY ; MAY NOT BE NEEDED
;CALL PC_SPACE ; *DEBUG*
CHSD_SETFNAME1:
;CALL DELAY
LD A,(DE) ; GET NEXT BYTE
INC DE ; BUMP POINTER
CALL CH_WR ; SEND IT
;CALL COUT ; *DEBUG*
OR A ; CHECK FOR NUL (EOS)
RET Z ; IF NUL, DONE
JR CHSD_SETFNAME1 ; SEND MORE CHARACTERS
;
; OPEN FILE
;
CHSD_FOPEN:
CALL CHSD_SETFNAME
;PRTS("\n\rFOPEN:$") ; *DEBUG*
LD A,CH_CMD_FOPEN ; FILE OPEN COMMAND
CALL CH_CMD ; SEND IT
CALL CH_POLL ; WAIT FOR RESULT
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEXBYTE ; *DEBUG*
CP $42 ; MISSING FILE?
JP Z,CHSD_NOFILE ; HANDLE ERROR
CP $14 ; SUCCESS?
JP NZ,CHSD_IOERR ; HANDLE ERROR
RET ; RETURN WITH ZF SET APPROPRIATELY
;
; GET FILE SIZE
;
CHSD_FILESIZE:
;PRTS("\n\rFSIZE:$")
LD A,CH_CMD_FILESIZE ; FILE SIZE COMMAND
CALL CH_CMD ; SEND IT
LD A,$68 ; REQUIRED CMD PARAMETER
CALL CH_WR ; SEND IT
CALL CH_NAP
LD A,CHSD_MEDCAP ; MEDIA CAPACITY OFFSET
CALL LDHLIYA ; HL := IY + A, REG A TRASHED
PUSH HL ; SAVE ADDRESS
CALL CH_RD
LD L,A
CALL CH_RD
LD H,A
CALL CH_RD
LD E,A
CALL CH_RD
LD D,A
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEX32 ; *DEBUG*
LD B,9 ; ROTATE 9 BITS FOR DIV 512
CHSD_FILESIZE1:
SRL D
RR E
RR H
RR L
DJNZ CHSD_FILESIZE1 ; LOOP TILL DONE
POP BC ; RECOVER ADDRESS TO BC
;CALL PC_SPACE ; *DEBUG*
;CALL PRTHEX32 ; *DEBUG*
CALL ST32 ; STORE IT
XOR A ; SIGNAL SUCCESS
RET ; AND DONE
;
; ERROR HANDLERS
;
;
CHSD_NOFILE:
LD A,CHSD_STNOFILE
JR CHSD_ERR
;
CHSD_NOMEDIA:
LD A,CHSD_STNOMEDIA
JR CHSD_ERR
;
CHSD_CMDERR:
LD A,CHSD_STCMDERR
JR CHSD_ERR
;
CHSD_IOERR:
LD A,CHSD_STIOERR
JR CHSD_ERR
;
CHSD_TO:
LD A,CHSD_STTO
JR CHSD_ERR
;
CHSD_NOTSUP:
LD A,CHSD_STNOTSUP
JR CHSD_ERR
;
CHSD_ERR:
LD (IY+CHSD_STAT),A ; SAVE NEW STATUS
;
CHSD_ERR2:
#IF (CHSDTRACE >= 2)
CALL CHSD_PRTSTAT
#ENDIF
OR A ; SET FLAGS
RET
;
;
;
CHSD_PRTERR:
RET Z ; DONE IF NO ERRORS
; FALL THRU TO CHSD_PRTSTAT
;
; PRINT FULL DEVICE STATUS LINE
;
CHSD_PRTSTAT:
PUSH AF
PUSH DE
PUSH HL
LD A,(IY+CHSD_STAT)
CALL CHSD_PRTPREFIX ; PRINT UNIT PREFIX
CALL PC_SPACE ; FORMATTING
CALL CHSD_PRTSTATSTR
POP HL
POP DE
POP AF
RET
;
; PRINT STATUS STRING
;
CHSD_PRTSTATSTR:
PUSH AF
PUSH DE
PUSH HL
LD A,(IY+CHSD_STAT)
NEG
LD HL,CHSD_STR_ST_MAP
ADD A,A
CALL ADDHLA
LD E,(HL)
INC HL
LD D,(HL)
CALL WRITESTR
POP HL
POP DE
POP AF
RET
;
; PRINT DIAGNONSTIC PREFIX
;
CHSD_PRTPREFIX:
PUSH AF
CALL NEWLINE
PRTS("CHSD$")
LD A,(IY+CH_DEV) ; GET CURRENT DEVICE NUM
CALL PRTDECB
CALL PC_COLON
POP AF
RET
CHSD_DEVNUM .DB 0 ; TEMP DEVICE NUM USED DURING INIT
;
;=============================================================================
; DATA STORAGE
;=============================================================================
;
CH_FWVER .DW 0,0 ; MMNNBBB (M=MAJOR, N=MINOR, B=BUILD)
CHSD_DEVNUM .DB 0 ; TEMP DEVICE NUM USED DURING INIT
CHSD_DSKBUF .DW 0
;
CHSD_IOFNADR .DW 0 ; PENDING IO FUNCTION ADDRESS
CHSD_FNAME .DB "/", CHSD_IMGFILE, 0
;
CHSD_DSKBUF .DW 0
CHSD_STR_ST_MAP:
.DW CHSD_STR_STOK
.DW CHSD_STR_STNOMEDIA
.DW CHSD_STR_STCMDERR
.DW CHSD_STR_STIOERR
.DW CHSD_STR_STTO
.DW CHSD_STR_STNOTSUP
.DW CHSD_STR_STNOFILE
;
CHSD_DSKSTAT .DB 0
CHSD_ERRCODE .DW 0,0
CHSD_CSDBUF .FILL 16,0
;
CHSD_CMD .DB 0
;
CHSD_TIMEOUT .DW $0000 ; FIX: MAKE THIS CPU SPEED RELATIVE
CHSD_STR_STOK .TEXT "OK$"
CHSD_STR_STNOMEDIA .TEXT "NO MEDIA$"
CHSD_STR_STCMDERR .TEXT "COMMAND ERROR$"
CHSD_STR_STIOERR .TEXT "IO ERROR$"
CHSD_STR_STTO .TEXT "TIMEOUT$"
CHSD_STR_STNOTSUP .TEXT "NOT SUPPORTED$"
CHSD_STR_STNOFILE .TEXT "MISSING "
.TEXT CHSD_IMGFILE
.TEXT " FILE$"
CHSD_STR_STUNK .TEXT "UNKNOWN ERROR$"

View File

@@ -993,7 +993,8 @@ HBX_BUF_END .EQU $
.DW 0 ; HB_DSTADR: BNKCPY DESTINATION ADDRESS
.DB BID_USR ; HB_DSTBNK: BNKCPY DESTINATION BANK ID
.DW 0 ; HB_CPYLEN: BNKCPY LENGTH
.FILL 4,0 ; FILLER, RESERVED FOR FUTURE HBIOS USE
.DW 0 ; RESERVED FOR OPTIONAL TICK CTR, PLATFORM DEPENDENT
.DW 0 ; RESERVED FOR FUTURE HBIOS USE
.DB 0 ; SHADOW VALUE FOR RTC LATCH PORT
.DB $FE ; HB_LOCK: HBIOS MUTEX LOCK
JP HBX_INVOKE ; HB_INVOKE: FIXED ADR ENTRY FOR HBX_INVOKE (ALT FOR RST 08)
@@ -1601,51 +1602,43 @@ MBC_SINGLE:
;
#ENDIF
;
; IF ALREADY EXECUTING IN RAM, BYPASS RAM BANK INSTALLATION
;
LD A,(HB_RAMFLAG)
OR A
JR NZ,HB_START1
;
; IF BID_BOOT AND BID_BIOS ARE THE SAME, THEN IT IS NEVER APPROPRIATE
; TO COPY THE HBIOS IMAGE FROM BID_BOOT TO BID_BIOS. THIS IS TYPICALLY
; THE CASE FOR A ROMLESS SYSTEM.
;
#IF (BID_BOOT != BID_BIOS)
;
; INSTALL HBIOS IN RAM BANK
;
LD A,(HB_CURBNK)
;
; CHECK TO SEE IF WE ARE ALREADY RUNNING IN THE HBIOS
; BANK AND SKIP THE COPY IF SO (DON'T COPY OVER OURSELVES).
; THIS SITUATION OCCURS ON A ROMLESS STARTUP OR WHEN DOING A
; FULL RESTART OF A SYSTEM USING THE EXISTING HBIOS COPY.
CP BID_BIOS
JR Z,HB_START1
;
LD (HB_SRCBNK),A
LD A,BID_BIOS
LD (HB_DSTBNK),A
LD HL,0
LD DE,0
LD BC,$8000
#IF (MEMMGR == MM_Z280)
#IF (MEMMGR == MM_Z280)
CALL Z280_BNKCPY
#ELSE
#ELSE
CALL HBX_BNKCPY
#ENDIF
#ENDIF
;
; TRANSITION TO HBIOS IN RAM BANK
;
#IF (MEMMGR == MM_Z280)
#IF (MEMMGR == MM_Z280)
LD A,BID_BIOS
LD B,$10 ; FIRST SYSTEM PDR
CALL Z280_BNKSEL
JR HB_START1
#ELSE
#ELSE
LD A,BID_BIOS ; BIOS BANK ID
LD IX,HB_START1 ; EXECUTION RESUMES HERE
CALL HBX_BNKCALL ; CONTINUE IN RAM BANK, DO NOT RETURN
HALT ; WE SHOULD NOT COME BACK HERE!
#ENDIF
;
#ENDIF
;
HB_RAMFLAG .DB FALSE ; INITIALLY FALSE, SET TO TRUE BELOW AFTER RAM TRANSITION
;
; EXECUTION RESUMES HERE AFTER SWITCH TO RAM BANK
;
HB_START1: ; BNKCALL ARRIVES HERE, BUT NOW RUNNING IN RAM BANK
@@ -1859,11 +1852,13 @@ HB_CPU1:
;
; CLEAR DISPATCH TABLE ENTRIES
;
XOR A ; ZERO
LD (CIO_CNT),A ; CIO DEVICES
LD (DIO_CNT),A ; DIO DEVICES
LD (VDA_CNT),A ; VDA DEVICES
LD (SND_CNT),A ; SND DEVICES
XOR A ; ZERO
LD (CIO_CNT),A ; CIO DEVICES
LD (DIO_CNT),A ; DIO DEVICES
LD (VDA_CNT),A ; VDA DEVICES
LD (SND_CNT),A ; SND DEVICES
LD (RTC_DISPACT),A ; RTC DEVICE
LD (DSKY_DISPACT),A ; DSKY DEVICE
;
#IF (DSRTCENABLE)
CALL DSRTC_PREINIT
@@ -2062,7 +2057,7 @@ HB_CPU3:
LD A,(CB_CPUMHZ) ; CPU SPEED TO ACCUM AND INIT
CALL DELAY_INIT ; .. SPEED COMPENSATED DELAY
;
#IF (INTMODE == 2)
#IF ((INTMODE == 2) | ((INTMODE == 1) & (CPUFAM == CPU_Z180)))
; SETUP Z80 IVT AND INT MODE 2
LD A,HBX_IVT >> 8 ; SETUP HI BYTE OF IVT ADDRESS
LD I,A ; ... AND PLACE IT IN I REGISTER
@@ -2073,7 +2068,9 @@ HB_CPU3:
OUT0 (Z180_IL),A ; ... AND PLACE IN Z180 IL REGISTER
#ENDIF
#IF (INTMODE == 2)
IM 2 ; SWITCH TO INT MODE 2
#ENDIF
#ENDIF
;
#IF (INTMODE == 3)
@@ -2855,19 +2852,27 @@ HB_FP1:
;
HB_FP2:
; IF SEC SWITCH IS SET, WE WANT TO BUMP TO SECONDARY
; CRT OR SERIAL DEVICE. FOR NOW, WE ARE GOING TO CHEAT AND
; JUST INCREMENT THE CONSOLE DEVICE UNIT. THIS SHOULD WORK
; ASSUMING NORMAL ORDERING OF THE CHARACTER DEVICE UNITS.
; CRT OR SERIAL DEVICE. IF AN OVERRIDE IS SPECIFIED USING
; SECCON, USE THAT, OTHERWISE INCREMENT THE DEFAULT UNIT.
; THIS SHOULD WORK ASSUMING NORMAL ORDERING OF THE
; CHARACTER DEVICE UNITS.
LD A,B ; RECOVER SWITCH SETTINGS
AND SW_SEC ; TEST SEC BIT
JR Z,HB_FPZ ; IF NOT SET, THEN ALL DONE
;
; INCREMENT CONSOLE UNIT, BUT MAKE SURE IT DOES NOT EXCEED
; THE HIGHEST CHAR UNIT IN SYSTEM.
LD A,(CIO_CNT) ; GET CHAR UNIT COUNT
LD B,A ; MOVE TO B
LD A,SECCON ; GET SEC CONSOLE SETTING
CP $FF ; $FF MEANS USE INCREMENT
JR NZ,HB_FP3 ; BYPASS IF NOT $FF
;
; INCREMENT CONSOLE UNIT
LD A,(HB_NEWCON) ; GET NEW CONSOLE UNIT
INC A ; BUMP TO SECONDARY
;
HB_FP3:
; MAKE SURE NEW CONSOLE UNIT DOES NOT EXCEED THE HIGHEST
; CHAR UNIT IN SYSTEM.
CP B ; A (UNIT) >= B (CNT)?
JR NC,HB_FPZ ; ABORT IF UNIT TOO HIGH
LD (HB_NEWCON),A ; UPDATE NEW CONSOLE UNIT
@@ -5423,10 +5428,10 @@ HB_BADINTCNT .DB 0
LD A,L
RRCA
RRCA
CALL PRTHEXBYTE
PRTS("H: $")
;CALL PRTHEXBYTE
;PRTS("H: $")
CALL XREGDMP
CALL NEWLINE
;CALL CONTINUE
OR $FF ; SIGNAL INTERRUPT HANDLED
RET

View File

@@ -303,6 +303,14 @@ BE_AGAIN:
BE_END:
HB_EI
POP IX
;
; Above flow flips the speaker bit an odd number of times which
; leaves the bit set to the opposite value it started at. This
; ensures that the bit is properly reset to its original value.
;
LD A,(HB_RTCVAL) ; Get the current RTC latch value
OUT (RTCIO),A ; Set it
;
RET ; ALWAYS EXITS WITH SUCCESS STATUS (A=0)
;
;======================================================================

View File

@@ -59,6 +59,7 @@ UARTSBASE .EQU $58
#ELSE
UARTSBASE .EQU $68
#ENDIF
UARTABASE .EQU $A8
UARTCBASE .EQU $80
UARTMBASE .EQU $18
UART4BASE .EQU $C0
@@ -1041,6 +1042,16 @@ UART_CFG_SBC:
.DW UARTCFG ; LINE CONFIGURATION
.DW UARTSBC_RCVBUF ; POINTER TO RCV BUFFER STRUCT
#ENDIF
#IF (UARTAUX)
UART_CFG_AUX:
; AUX SERIAL PORT
.DB 0 ; DEVICE NUMBER (UPDATED DURING INIT)
.DB 0 ; UART TYPE
.DB UARTABASE ; IO PORT BASE (RBR, THR)
.DB UARTABASE + UART_LSR ; LINE STATUS PORT (LSR)
.DW UARTCFG ; LINE CONFIGURATION
.DW 0 ; NO INT HANDLER
#ENDIF
#IF (UARTCAS)
UART_CFG_CAS:
; CASSETTE INTERFACE SERIAL PORT

View File

@@ -407,8 +407,8 @@ XREGDMP:
LD (REGDMP_SP),SP ; SAVE STACK POINTER
;LD (RD_STKSAV),SP ; SAVE ORIGINAL STACK POINTER
;LD SP,RD_STACK ; SWITCH TO PRIVATE STACK
LD (RD_STKSAV),SP ; SAVE ORIGINAL STACK POINTER
LD SP,RD_STACK ; SWITCH TO PRIVATE STACK
PUSH AF
PUSH BC
@@ -462,7 +462,7 @@ XREGDMP:
POP BC
POP AF
;LD SP,(RD_STKSAV) ; BACK TO ORIGINAL STACK FRAME
LD SP,(RD_STKSAV) ; BACK TO ORIGINAL STACK FRAME
JP $FFFF ; RETURN, $FFFF IS DYNAMICALLY UPDATED
REGDMP_RET .EQU $-2 ; RETURN ADDRESS GOES HERE
@@ -470,9 +470,9 @@ REGDMP_RET .EQU $-2 ; RETURN ADDRESS GOES HERE
REGDMP_PC .DW 0
REGDMP_SP .DW 0
;
;RD_STKSAV .DW 0
; .FILL $FF,16*2 ; 16 LEVEL PRIVATE STACK
;RD_STACK .EQU $
RD_STKSAV .DW 0
.FILL $FF,16*2 ; 16 LEVEL PRIVATE STACK
RD_STACK .EQU $
;
;
;

Binary file not shown.

View File

@@ -17,6 +17,11 @@ FLASH4 has been tested and confirmed working on:
It should work on many other machines that run RomWBW or UNA BIOS. If you test
it on another machine please let me know the outcome.
FLASH030 (also included) is a Linux version of the same software. It is
targetted at my 68030 machine but should be very easy to port to other
machines. It expects a machine with a larger address space, and thus omits much
of the bank switching and other tricks required on Z80 platforms.
= Introduction =

Binary file not shown.

View File

@@ -1,282 +1,112 @@
File: UNARC.DOC
Subject: User Documentation for UNARC Program
Version: 1.6
Date: March 27, 1987
------------------------------------------------------------------------------
UNARC
CP/M Archive File Extraction Utility
Copyright (C) 1986, 1987 by Robert A. Freed
All Rights Reserved
This file provides user-level documentation and operating instructions for
UNARC version 1.6, released March 27, 1987. Refer to the notice at the end of
this file regarding rights of use and distribution of this program.
The release message file, UNARC.MSG, provides a list of all additional files
distributed with the current UNARC release and describes the program changes
from the previous version 1.4 and 1.5 releases.
ABSTRACT
--------
UNARC is a utility program for CP/M systems which allows the listing, typeout,
printing, checking, and extraction of subfiles contained in "archive" library
(*.ARC or *.ARK) files. These are commonly used for compressed file storage
on remote access bulletin board systems. UNARC provides the CP/M user the
ability to process such files after downloading them via modem from these
remote systems.
REQUIREMENTS
------------
UNARC requires CP/M version 2 or higher. The program is offered in two
versions. The standard version, UNARC.COM, requires a Z80 processor (or
compatible equivalent, e.g. HD64180 or NSC800). An alternate version,
UNARCA.COM, is provided for systems with 8080 or 8085 processors (or 16-bit
systems using the NEC V20 for CP/M emulation). Identical capabilities are
provided by the two program versions.
NOTE
Although UNARCA.COM can execute on ANY system capable of
supporting CP/M, it is larger and significantly slower than
UNARC.COM and should be avoided by users of Z80-based systems.
UNARC is written in Z80 assembly language and requires only 5K bytes of disk
storage (6K for UNARCA). As distributed, the program requires at least 30K
bytes of available memory space (TPA) for full support of all archive file
formats (31K TPA size for UNARCA). (Smaller systems may be able to use some
of the program's capabilities.)
ABOUT ARC/ARK FILES
-------------------
The files which UNARC processes utilize a format that was introduced by the
ARC shareware utility program, which executes on 16-bit computers running the
MS-DOS (or PC-DOS) operating system. This format has achieved widespread
popularity since the ARC program first appeared in March 1985, and it has
become the de facto standard for file storage on remote access systems
catering to 16-bit computer users. More recently this file format has
achieved increased popularity on RCP/M (Remote CP/M) systems.
NOTE
Most RCP/M system operators have adopted the convention of naming
CP/M archive files with the filetype ARK. This differentiates
these from MS-DOS archive files, which use the filetype ARC. This
is a naming convention only: There is no difference in format,
and UNARC will accept files of either type interchangeably.
An archive is a group of files collected together into a single file in such a
way that the individual files may be recovered intact. In this respect,
archives are similar in function to libraries (*.LBR files), which have been
commonplace on CP/M systems since 1982, when the original LU library utility
program was introduced by Gary P. Novosielski. (However, the two file formats
are not compatible.)
The distinguishing characteristic of an ARC archive is that its component
files are automatically compressed when they are added to the archive, so that
the resulting file occupies a minimum amount of disk space. Of course, file
compression techniques have also been commonplace in the CP/M world since
1981, when the public domain SQ and USQ "squeeze and unsqueeze" programs were
introduced by Richard Greenlaw.
The SQ/USQ programs and their numerous popular descendants utilize a well-
known general-purpose form of data compression (Huffman coding). This
technique, which is also utilized in ARC files, performs well for many text
files but often produces poor compression of binary files (e.g. object program
.COM files). The ARC program also provides an advanced data compression
method, which it terms "crunching." This method (which is based on the
Lempel-Ziv-Welch or "LZW" algorithm) performs better than squeezing in most
cases, often achieving 50% or better compression of ASCII text files, 15-40%
compression of binary object files, and as much as 95% compression of bit-
mapped graphics image files.
Five different methods are actually employed for storing files in an archive.
The method chosen for a particular file is the one which results in the best
compression for that file:
(1) No compression ("unpacked"). The file is stored in its original form.
(2) Run-length encoding ("packed"). Repeated sequences of 3-255 identical
bytes are compressed into a three-byte sequence.
(3) Huffman coding ("squeezed"). Each 8-bit byte (after run-length encoding)
is encoded by a variable number of bits, with bit length (approximately)
inversely proportional to the frequency of occurence of the corresponding
byte.
(4) LZW compression ("crunched"). Variable-length strings of bytes (in
theory, up to nearly 4000 bytes in length) are represented by a single
(maximum) 12-bit code (after run-length encoding).
(5) LZW compression ("squashed"). This is a variation of crunching which
uses (maximum) 13-bit codes (and no run-length encoding).
Note that since one of the five methods involves no compression at all, the
resulting archive entry will never be larger than the original file.
NOTE
The most recent release of the MS-DOS ARC program (version 5.20)
has eliminated squeezing as a compression technique. However,
UNARC continues to process squeezed files for compatibility with
archives created by earlier versions of ARC and by other MS-DOS
archiving programs (notably PKARC).
The squashed compression method was recently introduced by the
MS-DOS programs PKARC and PKXARC. UNARC can process files which
use this method, although it is not universally accepted by other
MS-DOS archive extraction programs (including ARC).
During its lifetime, the ARC program has undergone numerous revisions which
have employed different variations on some of the above methods, particularly
LZW compression. In order to retain compatibility with archives created by
earlier program revisions, ARC stores a "version" indicator with each file in
an archive. Based on this indicator, the latest release of the ARC program
can always extract files created by older releases (although it will only use
the latest data compression versions when adding new files to an archive).
NOTE
The current release of UNARC supports archive file versions
generated by all releases of the following MS-DOS programs through
(at least) the indicated program versions:
ARC 5.20 (24 Oct 86), by System Enhancement Associates, Inc.
ARCA 1.22 (13 Sep 86), by Wayne Chin and Vernon Buerg
ARCH 5.38 (26 Jun 86), by Les Satenstein
PKARC 2.0 (15 Dec 86), by Phil Katz (PKWARE, Inc.)
(UNARC does not recognize, but is unaffected by, the non-standard
archive and file commenting feature of PKARC.)
Although the above discussion has emphasized the origin of archive files for
the MS-DOS operating system, their use has recently spread to many other
systems. Programs compatible with MS-DOS ARC have appeared for UNIX, Atari
68000, VAX/VMS, and TOPS-20 systems. A CP/M utility for building archive
files will also be available in the near future.
For additional information about archive files and the MS-DOS ARC utility,
refer to the documentation file, ARC.DOC, which is available from most remote
access systems which utilize archive files. For additional information about
the LZW algorithm (and data compression methods in general), refer to the
article "A Technique for High-Performance Data Compression", by Terry A.
Welch, in IEEE Computer magazine, Vol. 17, No. 6, June 1984.
USING UNARC
-----------
The UNARC program provides an on-line help message, which is generated by
running the program with an empty command line:
A>UNARC
UNARC 1.6 27 Mar 87
CP/M Archive File Extractor
Usage: UNARC [d:]arcfile[.typ] [d:][afn] [N|P|C]
Examples:
B>UNARC A:SAVE.ARK *.* ; List all files in CP/M archive SAVE on drive A
B>UNARC A:SAVE.ARC *.* ; List all files in MS-DOS archive SAVE on drive A
A>UNARC SAVE ; Same as either of above
A>UNARC SAVE *.* N ; Same as above (no screen pauses)
A>UNARC SAVE *.DOC ; List just .DOC files
A>UNARC SAVE READ.ME ; Typeout the file READ.ME
A>UNARC SAVE READ.ME N ; Typeout the file READ.ME (no screen pauses)
A>UNARC SAVE A: ; Extract all files to drive A
A>UNARC SAVE B:*.DOC ; Extract .DOC files to drive B
A>UNARC SAVE C:READ.ME ; Extract file READ.ME to drive C
A>UNARC SAVE PRN.DAT P ; Print the file PRN.DAT (no formatting)
A>UNARC SAVE *.* C ; Check validity of all files in archive
As shown by this help display, the UNARC utility provides the following
capabilities:
(1) Listing the directory of an archive
(2) Extracting component files from an archive
(3) Typing the contents of a component file at the console
(4) Printing a component file directly on the CP/M list device
(5) Checking the validity of an archive and its component files
The particular operation to be performed is determined by the form of the file
parameter(s) in the command line, as described separately in the sections
which follow. The following characteristics apply to all operations:
The first command line parameter must specify the name of an archive file. A
drive name and filetype are optional. The filetype, if omitted, defaults to
"ARK" or, if no such file exists, the alternate (MS-DOS) default "ARC" is
assumed.
The standard CP/M terminal control characters, CTRL-S (to suspend console
output) and CTRL-C (to abort the program), may be used at any time. CTRL-K
may also be used as an alternate for CTRL-C. Printer output to the CP/M list
device may be obtained by typing CTRL-P at CCP command level before executing
UNARC.
In addition, by default UNARC will pause after every 23 lines of console
output. At this time, the message "[more]" will appear at the bottom of the
console screen. The listing may be resumed by typing any key (other than
CTRL-S, CTRL-C, or CTRL-K, which will function as described above). If the
space bar is used, one more line of console output will be displayed (over-
writing the "[more]" message) and the program will again pause. If any other
key is typed (e.g. RETURN), another 23 lines of output will be allowed to
scroll onto the screen before the next pause. (LINE FEED may be used to
prevent overprinting of the "[more]" line, e.g. for hard-copy terminals.)
If continuous display is desired, this automatic pause feature may be disabled
by specifying "N" at the end of the command line. The "N" must be the last
command line character, and it must be preceded by a space. Also, there must
be two preceding file parameters on the command line. E.g., note the
difference between the following commands:
A>UNARC SAVE N ; Typeout the file N. in archive SAVE
A>UNARC SAVE *.* N ; List all files in archive SAVE with no pauses
The N option may not be used in conjunction with the P (Print) or C (Check)
options.
LISTING AN ARCHIVE DIRECTORY
----------------------------
By default, UNARC produces a detailed console listing of the component files
in an archive. (In fact, there is no way to suppress this listing; it is
generated during all UNARC operations.) If only the archive file name appears
on the command line, UNARC will generate a complete directory of all component
files in the specified archive file. Otherwise, the second command line
parameter may be used to select a particular file to be listed (or group of
files, if it contains the ambiguous file specification characters "*" or "?").
If no disk drive name is provided for the second parameter, and this parameter
specifies a group of files, the directory listing is the only output generated
by the program.
A sample directory listing is illustrated here:
A>UNARC CODES
Archive File = CODES.ARK
UNARCU
Universal Archive File Extraction Utility
Version 1.0
Modified for Universal use by Lars Nelson
September 17, 2023
Modified for ZCPR3 by Gene Pizzetta
December 9, 1990
Original CP/M 2.2 version is
Copyright (C) 1986, 1987 by Robert A. Freed
All Rights Reserved
UNARCU allows the listing, typeout, printing, checking, and extraction of
member files contained in ARK and ARC archive files. These are commonly
used for compressed file storage on remote access bulletin boards. This is
a universal version and runs on the following CP/M compatible systems:
CP/M 2.2 with DRI CCP or ZCPRD&J
ZSDOS 1.2 and 2.0 with DRI CCP, ZCPRD&J or Zsystem
CP/M 3 with DRI CCP or Z3Plus
ZPM3 with DRI CCP or ZCCP
DU file specification is supported on all systems. If Zsystem is active
then named directories can be used and the bad directories flag is
automatically checked.
If datestamping is available then extracted files will recieve the ARK file's
stored date stamp. The program handles DateStamper, NZTIME and CP.M Plus
date stamping methods.
UNARCU requires at least 32K of free memory (TPA) for full support of all
archive file formats, but smaller systems may be able to use some of the
program's capabilities.
USAGE:
UNARCU {DU: or dir:}arcfile{.typ} {DU: or dir:}{afn.aft} {{/}options}
If a DIR or DU specification is not given for the archive file, the current
drive/user is assumed. The second filename, which can be ambiguous,
refers to a member file or files in the archive. DIR: file specification
only available when Zsystem is active. DU: specification always available.
If a DU or DIR specification is provided for the member filespec, it will be
extracted to that directory. To extract to the current directory, only a
colon is required. If a directory specification is given without a filename,
all files ("*.*") is assumed.
If no DU or DIR specification is given, UNARCU acts differently depending
on whether the member name is ambiguous or not. If the member name is
unambiguous, and the filetype is not restricted, the file will be typed to
the screen. If the member name is ambiguous, or if no member name is
given at all, a directory of the ARK will be displayed.
If no filetype is given for the archive file, UNARCU first tries ARK and then
ARC.
An on-line help message will be displayed if UNARCU is called with no
command tail or if the command tail is "//".
OPTIONS: Options may or may not be preceded by a slash, but the slash is
required if the options are not the third token (element) on the command
line.
C Check the validity of the archive and the given member
files. If a member filespec is not given, all files
("*.*") is assumed.
E Toggle erasing of existing files without asking on and
off. UNARCU may be configured to automatically erase,
during member file extraction, existing files in the
target directory that have the same name. Or it can
be configured to ask first. This option will turn off
user query before erasure, if it is on by default, and
vice versa.
N Toggle console paging on or off. UNARCU may be
configured to default to console paging or not. This
option will turn paging off, if the the default is on,
and vice-versa. Paging effects both archive directory
display and member file type-out. During member file
extraction, console paging is always off.
P Sends a member file to the printer (LST device). The
member name cannot be ambiguous. The file will be
printed continuously, with no formatting or paging.
UNARCU can be aborted at any time with ^C or ^K.
If screen paging is enabled, UNARCU pauses after the screen fills. The
listing may be resumed by typing any key other than ^S, ^C, or ^K. The
space bar displays one more line of output (overwriting the "[more]"
message) and the program will again pause. For hard copy terminals, line
feed may be used to prevent overprinting of the "[more]" line. If paging
is disabled, the display can be paused with ^S.
LISTING AN ARCHIVE DIRECTORY: UNARC always produces a detailed
console listing of all the member files of an archive, or of those members
which match the second file specification, if one is given. If no member
name is given, or if the member name is ambiguous, then UNARCU only lists
the directory, without doing anything else. (That is, unless the C option is
included.)
A sample directory listing:
A0>UNARCU CODES
Archive File = A0:CODES.ARK
Name Length Disk Method Ver Stored Saved Date Time CRC
============ ======= ==== ======== === ======= ===== ========= ====== ====
ABLE .DOC 24320 24k Crunched 8 11777 52% 30 Apr 86 10:50a 42C0
@@ -285,97 +115,82 @@ CHARLIE .TXT 234 1k Packed 3 99 58% 2 May 86 4:11p 8927
==== ======= ==== ======= === ====
Total 3 41706 42k 26626 36% 58A4
The listing is equivalent to the "verbose" listing of the MS-DOS ARC
program, with the addition of the "Disk" and "Ver" fields, which are unique
to UNARCU and previous UNARC versions. The listing requires 78-columns
of terminal width.
This listing is equivalent to the "verbose" listing of the MS-DOS ARC program
(with the addition of the "Disk" and "Ver" fields, which are unique to UNARC).
The listing requires a 78-column terminal width; there is currently no "short"
listing format.
"Name" is the filename which will be generated if the file is extracted by
UNARCU. This is not necessarily the same as the name recorded in the
archive file. Although CP/M and MS-DOS file naming conventions are
identical, two conversions are made to guarantee filename validity: Lower-
case letters are converted to upper-case and non-printing characters are
converted to dollar signs ("$"). Archive entries are usually maintained and
listed in alphabetical order.
"Name" is the file name which will be generated if the file is extracted by
UNARC on a CP/M system. (This is not necessarily the same as the name
recorded in the archive file. Although CP/M and MS-DOS file naming
conventions are identical, two conversions are made to guarantee file name
validity under CP/M: Lower-case letters are converted to upper-case, and
non-printing characters are converted to dollar signs, "$".) Archive entries
are usually maintained (and hence listed) in alphabetic name order.
"Length" is the uncompressed file length, i.e., the number of bytes the file
will occupy if extracted to disk, exclusive of any additional length imposed
by the file system. MS-DOS permits files of arbitrary lengths, but CP/M
restricts files to multiples of 128 bytes.
"Length" is the uncompressed file length, i.e. the number of bytes the file
will occupy if extracted to disk, exclusive of any additional length imposed
by the CP/M file system. Note that MS-DOS permits files of arbitrary lengths
(unlike CP/M which restricts all files to a multiple of 128 bytes).
"Disk" is the actual amount of space required to extract the file to a CP/M
disk, expressed as a multiple of 1K (1024) bytes. The number is dependent
on the output drive's allocation block size, which can range from 1K to 16K
bytes. Typically, 1K is used for single-density floppy disks, 2K for
double-density floppies, and 4K for hard disks. In the absence of an
explicit output drive, UNARCU uses the block size of the currently logged
drive, or a configured default size.
"Disk" is the actual amount of disk space required to extract the file to a
CP/M disk, expressed as a multiple of 1K (1024) bytes. Note that this number
is dependent on the disk data allocation block size. (CP/M permits various
block sizes, ranging from 1K to 16K bytes. Typical sizes are 1K for single-
density floppy disks, 2K for double-density floppies, and 4K for hard disks,
although these values are quite system-dependent.) In the absence of an
explicit output drive name, UNARC uses the block size of the default
(currently "logged") disk drive (i.e. the drive which appears in the CCP
prompt).
"Method" is the compression method used: "Unpacked", "Packed",
"Squeezed", "Crunched", "Squashed", or "Unknown!". If the method
"Unknown!" appears, it likely indicates a faulty archive file or a newer
compression method not yet supported by UNARCU.
"Method" is the compression method used, specified as "Unpacked", "Packed",
"Squeezed", "Crunched", "Squashed", or "Unknown!". If the method "Unknown!"
appears, it most likely indicates (if not a faulty archive file) a newer
release of the MS-DOS ARC program that supports a new compression method (or a
new variation of an existing method). In this case, a corresponding new
release of UNARC will be required to extract the file.
"Ver" is the version of compression method used. UNARC supports versions
1-9: unpacked files, versions 1 or 2; packed files, version 3; squeezed
files, version 4; crunched files, versions 5 and squashed files, version 9.
"Ver" further identifies the version of compression used. Currently, UNARC
supports versions 1-9: unpacked files can have versions 1 or 2; packed files,
version 3; squeezed files, version 4; crunched files, versions 5-8; and
squashed files, version 9. The highest version number associated with each
compression method is the one generated by the most recent release of the
MS-DOS ARC program.
"Stored" is the compressed file length, that is, the number of bytes
occupied by the file in the archive, not including the directory information
overhead, which adds an additional 29 bytes to each member file.
"Stored" is the compressed file length, i.e. the number of bytes occupied by
the file in the archive. (This does not include the overhead associated with
the directory information itself, which adds an additional 29 bytes to the
size of each component file.)
"Saved" is the percentage of the original file length which was saved by
compression; i.e., higher values indicate better compression. (The MS-DOS ARC
documentation refers to this as the "stowage factor.") The value shown on the
totals line applies to the archive as a whole, not including the directory
"Saved" indicates the percentage of the original file length which was saved
by compression. Higher values indicate better compression. The MS-DOS
ARC documentation refers to this as the "stowage factor". The value shown
in the totals applies to the archive as a whole, excluding directory
overhead.
"Date" and "Time" refer to the last file modification, as of the time it was
added to the archive. (Date and time stamping is, of course, one of the nice
features of MS-DOS which is lacking in standard CP/M 2.2.)
"Date" and "Time" are the file modification stamp at the time it was added
to the archive.
"CRC" is an internal 16-bit cyclic redundancy check value which is computed
when a file is added to an archive (expressed in hexadecimal). As a test of
file validity, UNARC re-computes this value when it extracts a file (see
below). Note that this value is calculated by a different method than that
used by either of the two popular public domain programs, CRCK and CHEK. (It
is however quite valid as a reliable error-detection mechanism.) This value
is shown in the listing for completeness only. The value shown on the totals
line is the 16-bit sum of all displayed CRC values. This is useful as a
single "checksum" value for comparing entire archives. (Since the CRC values
are computed before compression takes place, the total should be the same for
all archives created from the same set of input files, independent of any
particular variations in file order or compression methods.)
"CRC" is an internal 16-bit cyclic redundancy check value computed when a
file is added to an archive, expressed in hexadecimal. UNARCU checks file
validity by recomputing this value when it extracts a file. The value is
calculated by a different method than that used by either of the two
popular public domain programs, CRCK and CHEK, but it is a quite valid and
reliable error-detection mechanism. The value is given for completeness
only. The total in the last line is the 16-bit sum of the displayed CRC
values and is useful for comparing entire archives. Since the CRC values
are computed before compression, the total should be the same for all
archives created from the same set of input files, without regard for
variations in file order or compression methods.
The "Total" line is displayed only if multiple files appear in the listing,
and it includes a count of the number of files listed.
The "Total" line is displayed only if more than one file appears in the
listing.
EXTRACTING FILES FROM AN ARCHIVE: If the second command line
parameter contains a DU or DIR specification UNARCU will extract the
selected member file or files to to the indicated disk directory. If the
directory specification is given without a filename, all member files will be
extracted to the indicated directory. If only a colon is given, the current
drive/user will be assumed.
Below is a directory listing as might be generated during file extraction,
along with some possible warning messages:
EXTRACTING FILES FROM AN ARCHIVE
--------------------------------
If the second command line parameter contains a disk drive name, UNARC will
extract the selected file(s) from the archive to CP/M file(s) on the indicated
disk drive. If only a drive name appears, all component files of the archive
will be extracted. The following illustrates a sample archive directory
listing as generated during a file extraction operation:
A>UNARC CODES B:
Archive File = CODES.ARK
Output Drive = B:
A0>UNARCU CODES B1:
Archive File = A0:CODES.ARK
Output Directory = B1:
Name Length Disk Method Ver Stored Saved Date Time CRC
============ ======= ==== ======== === ======= ===== ========= ====== ====
ABLE .DOC 24320 24k Crunched 8 11777 52% 30 Apr 86 10:50a 42C0
@@ -388,236 +203,235 @@ CHARLIE .TXT 234 2k Packed 3 99 58% 2 May 86 4:11p 8927
==== ======= ==== ======= === ====
Total 3 41706 44k 26616 36% 58A4
"Replace existing output file (y/n)?" appears if a file of the same name
exists in the output directory, requiring a "Y" or "N" response. Any
response other than "Y" will be consided to be the same as "N". If UNARCU
has been configured to erase without query, this message will not appear.
The above listing also illustrates several warning messages which may occur
when extracting files from an archive.
The first two of the "Warning:" messages above indicate that either the
cyclic redundancy check (CRC) value or the extracted file length does not
match the value recorded in the archive header when the original file was
added. The third warning message is displayed if the proper format for
the beginning of a new member is not detected, but UNARCU recovered by
skipping a certain number of bytes in the archive file. If a recovery
attempt fails, UNARC aborts and issues a different message, "Invalid archive
file format". The appearance of any of these messages probably means the
file data has been corrupted in some way.
The message "Replace existing output file (y/n)?" appears if a file of the
same name already exists on the output drive. The user must answer "Y" (or
"y") to allow the extraction to proceed (in which case, the existing file is
unceremoniously deleted). Any other response will cause UNARC to preserve the
existing file, bypass the extraction operation for the current file, and
(except for a CTRL-C response) skip to the next file to be extracted (if any).
If the original MS-DOS file length was not an exact multiple of 128 bytes,
the final record of the extracted file will be padded with 1Ah characters
(ASCII ^Z).
The first two warning messages illustrated above are provided as a check on
the validity of the extracted file. These indicate that either the cyclic
redundancy check (CRC) value computed by UNARC, or the resulting extracted
file length, does not match the corresponding value recorded in the archive
when the original file was added to it. The final warning message occurs if
UNARC fails to detect the proper format for the start of a new subfile, but
can recover by skipping a certain number of bytes in the archive file. (If
the recovery attempt fails, UNARC aborts with the message "Invalid archive
file format.") The appearance of any of these messages most likely indicates
that the file data has been corrupted in some way (e.g. during modem
transmission from a remote system).
Disk space in the listing will be correct for the specified output directory.
In the two examples above, drive A has 1K allocation blocks while drive B
has a 2K blocks, which accounts for the differences in the two listings. To
determine the exact disk space requirements before extracting files, log
into the desired output drive and take an UNARCU directory listing of the
ARK file.
Note that if the original (i.e. MS-DOS) file length was not an exact multiple
of 128 bytes (as required by CP/M), UNARC will pad the final record of the
extracted file with hex "1A" (ASCII CTRL-Z) bytes. This provides the correct
end-of-file termination for text files, according to CP/M conventions.
If a file extraction is aborted with ^C, any partial output file will have to
be deleted manually.
Also, the disk space shown in the archive directory listing will be correct
for the specified disk drive. (In the above examples, drive A: has a 1K data
allocation block size while drive B: has a 2K block size, which accounts for
the differences in the two listings.) In order to determine the exact disk
space requirements in advance of a file extraction operation, the user may
first "log into" the desired output drive (i.e. select it as the default
drive), and run UNARC to obtain a directory listing only. (This is a
consideration only on systems with mixed disk drive types.)
A file extraction operation may be aborted at any time by entering CTRL-C from
the console. In this case, any partial output file will remain on disk and
should be deleted manually following the program abort. (Any existing file of
the same name will have already been deleted, however.)
TYPING OUT A FILE IN AN ARCHIVE
-------------------------------
A console typeout of the contents of a single component file in an archive may
be requested by specifying a non-ambiguous file name (and no disk drive name)
in the second command line parameter. For example:
A>UNARC CODES ABLE.DOC
Archive File = CODES.ARK
TYPING MEMBER FILES: Typing the contents of a member file in an archive
to the console may be requested by giving a non-ambiguous filename and no
output disk directory as the second command line parameter. For example:
A0>UNARCU CODES ABLE.DOC
Archive File = A0:CODES.ARK
Name Length Disk Method Ver Stored Saved Date Time CRC
============ ======= ==== ======== === ======= ===== ========= ====== ====
ABLE .DOC 24320 24k Crunched 8 11777 52% 30 Apr 86 10:50a 42C0
-------------------------------------------------------------------------------
This is file ABLE.DOC, contained within the archive CODES.ARK. Typeout will
proceed until the end of this file or may be aborted by CTRL-C.....
proceed until the end of this file, so you'd better be patient. For somebody
who has nothing to say, I've written an awfully big file here. If you don't
want to read all 24K of it, you can type ^C ....
The specified file is assumed to contain valid ASCII text data. All bytes
are masked to seven bits and all control characters are ignored except
horizontal tabs, which are expanded to blanks with stops at every eighth
column), and line feeds, vertical tabs, and form feeds, all of which generate
a new line. SUB (^Z) is interpreted as the end of the file. Backspaces and
carriage returns are ignored, so text will not be obscured.
UNARCU will refuse to type files whose filetype indicates are not ASCII text
files, including COM, CMD, EXE, OBJ, OVL, REL, PRL, CRL, IRL, INI, SYS,
BAD, ARK, ARC, LBR, ?Q?, ?Y? and ?Z?. If one of these or other restricted
types is given, directory information only is listed.
CRC and file length checking are not performed when a file is typed to the
screen.
PRINTING MEMBER FILES: A single member file may be sent to the printer
(CP/M LST device) with the "P" option as the third parameter on the
command line with or without a preceding slash. In addition, the member
name must be non-ambiguous and must not be preceded by a drive or user
specification. For example:
A0>UNARCU CODES CHARLIE.TXT P
or
A0>UNARCU CODES CHARLIE.TXT /P
The contents of the specified file is passed directly to the printer without
alteration, additional formatting, or even paging. The user should make
sure it contains data suitable for printer output. This unfiltered operation
is particularly well-suited for the output of binary graphics images to
dot-matrix printers. These files can be extremely large, but compress quite
well, often to less than 5% of their original size. The same filetypes
excluded from typing are also excluded from printing. Printing may be
paused or aborted with ^S and ^C respectively.
CHECKING MEMBER FILES: With the "C" option UNARCU can be directed to
extract one or more member files from an archive, without actually storing
them as disk files. This operation performs file CRC and length checking,
so it is useful for verifying correct modem data transmission of an archive.
If the "C" is the second parameter on the command line, it must be
preceded by a slash. In that case all files in the archive will be checked.
If a member filename is given, it may be ambiguous, but it cannot be
preceded by a disk directory specification. For example:
A0>UNARCU CODES *.DOC C
or
A0>UNARCU CODES /C
FILE DATE STAMPING: ARK and ARC files contain only a member file's
modification date and time. When a member is extracted under ZSDOS or
CP/M 3 with date stamping, its modification date will be transferred to disk
as both the create and modification file date stamps. If the modification
date is not included in the archive, then the extracted file will be stamped
with the current date and time.
SECURITY: Z-Node security is handled automatically by UNARCU when
Zsystem is running. If the Wheel byte is off (reset), file extraction,
archive checking, and file printing are all disabled. In addition, UNARCU
can be configured to disable file type-out or to limit type-out to a maximum
number of lines.
The specified file is assumed to contain valid ASCII text data. In
particular, all bytes are masked to seven bits, and all ASCII control
characters are ignored except for HT (horizontal tab, which is expanded to
blanks with assumed tab stops at every eighth column), LF, VT or FF (line
feed, vertical tab or form feed, which generate a new typeout line), and SUB
(CTRL-Z, which by CP/M convention indicates end-of-file and terminates the
typeout). Note that BS (backspace) and CR (carriage return) are ignored, so
that text will not be obscured within files which utilize these for over-
printing (i.e. when directed to a printer).
Directory security depends on the file specification parsing of ZCPR 3.3 or
higher to indicate that the DU or DIR are illegal. Security should be
adequate, however, under other CPR's.
The following filetypes, which are usually associated with binary (non-text)
data, are specifically excluded from typeout operations: COM, EXE, OBJ, OV?,
REL, ?RL, INT, SYS, BAD, LBR, ARC, ARK, ?Q?, and ?Z?. If one of these types
is specified, only the directory information for the requested file is listed.
PROGRAM CONFIGURATION OPTIONS: Several configuration bytes are
available to tailor the program for specific requirements, particularly for
RCP/M systems. With the Wheel byte off, UNARCU can be used by remote
callers only for archive directory listing and, optionally, for member file
typeout.
Note that CRC and file length checking are not performed during a typeout
operation, as they are during extraction to a disk file.
Configuration bytes also determine the default conditions for the N and E
command line options and the filetypes excluded from type-out.
Other configuration points are provided for non-standard systems and need
not concern the majority of users running ZCPR3, NZ-COM, or Z3PLUS.
Patching is accomplished using ZCNFG and the configuration file,
UNARCUnn.CFG, where nn is the current version. The options are discussed
in detail in the CFG file help screens. ZCNFG will find the CFG file
automatically, even if you change the name of the program, as long as you
do not change the name of the CFG file.
PRINTING A FILE IN AN ARCHIVE
-----------------------------
For most users no configuration is necessary.
A single component file in an archive may be output directly to the printer
(CP/M list device) by specifying a trailing "P" on the command line. The "P"
must be the last command line character, and it must be separated from the
second file parameter by a space. (The file parameter must specify a non-
ambiguous file name and no disk drive name.) For example:
ABOUT ARC/ARK FILES: The files which UNARCU processes utilize a format
that was introduced by the ARC shareware utility program, which executes
on 16-bit computers running the MS-DOS (or PC-DOS) operating system.
This format has achieved widespread popularity since the ARC program
first appeared in March 1985, and it has become the de facto standard for
file storage on remote access systems catering to 16-bit computer users.
This file format also achieved popularity on RCP/Ms (Remote CP/M) systems.
While ARC files have given way to ZIP files in general, many ARC files are
available on the web containing CP/M software.
A>UNARC CODES CHARLIE.TXT P
RCP/M system operators adopted the convention of naming CP/M archive
files with the filetype ARK. This differentiates these from MS-DOS archive
files, which use the filetype ARC. This is a naming convention only; there
is no difference in format, and UNARC will accept files of either type
interchangeably.
The specified file is assumed to contain data suitable for printer output and
is passed directly to the printer without alteration or additional formatting.
This operation is particularly well-suited for output of binary graphics
images on dot-matrix printers, since these can be extemely large but tend to
compress quite well (e.g. to less than 5% of their original size). Note that
the binary data filetypes which are excluded from typeout operations are also
excluded from printing operations. Printing may be paused or aborted by use
of the console CTRL-S and CTRL-C characters.
An archive is a group of files compressed and collected together into a
single file in such a way that the individual files may be recovered intact.
In this respect, archives are similar in function to libraries (LBR files),
which have been commonplace on CP/M systems since 1982, when the
original LU library utility program was introduced by Gary P. Novosielski.
The two file formats, however, are not compatible.)
The distinguishing characteristic of an ARC archive is that its component
files are automatically compressed when they are added to the archive, so
that the resulting file occupies a minimum amount of disk space. Of
course, file compression techniques have also been commonplace in the CP/M
world since 1981, when the public domain SQ and USQ "squeeze and
unsqueeze" programs were introduced by Richard Greenlaw.
The SQ/USQ programs and their numerous popular descendants utilize a
well-known general-purpose form of data compression (Huffman coding).
This technique, which is also utilized in ARC files, performs well for many
text files but often produces poor compression of binary files (e.g., object
program COM files). The ARC program also provides an advanced data
compression method, which it terms "crunching." This method (which is
based on the Lempel-Ziv-Welch or "LZW" algorithm) performs better than
squeezing in most cases, often achieving 50% or better compression of ASCII
text files, 15-40% compression of binary object files, and as much as 95%
compression of bit-mapped graphics image files.
CHECKING FILES IN AN ARCHIVE
----------------------------
Five different methods are actually employed for storing files in an
archive. The method chosen for a particular file is the one which results
in the best compression for that file:
1. No compression ("unpacked"). The file is stored in its
original form.
2. Run-length encoding ("packed"). Repeated sequences of 3-
255 identical bytes are compressed into a three-byte sequence.
3. Huffman coding ("squeezed"). Each 8-bit byte (after run-
length encoding) is encoded by a variable number of bits, with
bit length (approximately) inversely proportional to the
frequency of occurence of the corresponding byte.
4. LZW compression ("crunched"). Variable-length strings
of bytes (in theory, up to nearly 4000 bytes in length) are
represented by a single (maximum) 12-bit code (after run-length
encoding).
5. LZW compression ("squashed"). This is a variation of
crunching which uses (maximum) 13-bit codes (and no run-length
encoding).
UNARC may be directed to extract one or more component files from an archive,
without actually storing these as disk files, by specifying a trailing "C" on
the command line. This operation performs file CRC and length checking, and
it is useful for verifying correct modem data transmission of an archive. The
"C" must be the last command line character, and it must be separated from the
second file parameter by a space. (The file parameter must not specify a disk
drive name, which indicates extraction to disk.) To check an entire archive,
specify "*.*" for the second file parameter, for example:
Since one of the five methods involves no compression at all, the resulting
archive entry will never be larger than the original file.
A>UNARC CODES *.* C
The last release of the MS-DOS ARC program (version 5.20) has eliminated
squeezing as a compression technique. However, UNARC continues to
process squeezed files for compatibility with archives created by earlier
versions of ARC and by other MS-DOS archiving programs (notably PKARC).
The squashed compression method was introduced by the MS-DOS programs
PKARC and PKXARC. UNARC can process files which use this method,
although it is not universally accepted by other MS-DOS archive extraction
programs (including ARC).
During its lifetime, the ARC program has undergone numerous revisions
which have employed different variations on some of the above methods,
particularly LZW compression. In order to retain compatibility with
archives created by earlier program revisions, ARC stores a "version"
indicator with each file in an archive. Based on this indicator, the latest
release of the ARC program can always extract files created by older
releases (although it will only use the latest data compression versions when
adding new files to an archive).
PROGRAM OPTIONS
---------------
The current release of UNARC supports archive file versions generated by
all releases of the following MS-DOS programs through (at least) the
indicated program versions:
ARC 5.20 (24 Oct 86), by System Enhancement Associates, Inc.
ARCA 1.22 (13 Sep 86), by Wayne Chin and Vernon Buerg
ARCH 5.38 (26 Jun 86), by Les Satenstein
PKARC 2.0 (15 Dec 86), by Phil Katz (PKWARE, Inc.)
UNARC does not recognize, but is unaffected by, the non-standard archive
and file commenting feature of PKARC.
UNARC provides several options which may be used to tailor the program for
specific non-universal requirements. Many of these are intended for RCP/M
(Remote CP/M) system operators, to allow generation of a secure version of
UNARC which can be used by remote callers for purposes of archive directory
listing and/or file typeout only (but not file extraction). Others are
provided for specialized non-standard CP/M systems and need not concern the
majority of users running CP/M 2.2, CP/M 3.0 (CP/M Plus), or ZCPR3/ZRDOS
systems. Additional options provide user preference features (such as the
number of screen lines between console output pauses, or the list of filetypes
excluded from typeout operations).
Although the above discussion has emphasized the origin of archive files
for the MS-DOS operating system, their use did spread to many other
systems. Programs compatible with MS-DOS ARC have appeared for UNIX,
Atari 68000, VAX/VMS, and TOPS-20 systems. A CP/M utility for building
archive files is also available.
All of these options are described in UNARCOVL.ASM, an assembly language
source file that can be edited and assembled to generate a HEX-format overlay
for easy patching of the UNARC.COM or UNARCA.COM program files. Complete
details are provided for technically-oriented users in UNARCOVL.ASM. However,
the default options in the distributed program files are suitable for the
majority of users with standard CP/M operating systems.
PROGRAM DISTRIBUTION
--------------------
The UNARC program, its documentation, and all related files are distributed in
archive file format (of course!). The distribution file is named UNARCxx.ARK,
where "xx" is derived from the current version number (e.g. UNARC16.ARK for
version 1.6). (This does not include the program source code, which is
distributed separately.) This archive has the special characteristic that it
is "self-unpacking." I.e., a separate copy of the UNARC.COM program file is
NOT required to extract the component files from this archive.
The procedure for extracting the distribution files is quite simple: First,
copy or rename UNARCxx.ARK to a program file, UNARCxx.COM, on the current disk
drive. (Note that the filename, UNARCxx, must NOT be changed.) Then, run
this program with a single optional command line parameter specifying the disk
drive to which all distribution files will be extracted (defaults to current
drive).
For example, assuming UNARC16.ARK is on drive B: and the files are to be
extracted to drive C:, the following CP/M commands may be used:
A>B: ; Set current drive for UNARC16.ARK
B>REN UNARC16.COM=UNARC16.ARK ; Rename it to UNARC16.COM
B>UNARC16 C: ; Run it to extract all files to drive C:
Note that this self-unpacking capability is provided only by the distributed
archive file, and it will not work if that file is altered or reconstructed.
AUTHOR'S NOTE
-------------
I undertook writing the UNARC program to satisfy my curiosity about software
developments in the MS-DOS/PC-DOS world. At the time I began work on UNARC,
the MS-DOS ARC program had been in existence for over a year and had achieved
widespread popularity and acceptance in the 16-bit community. Unfortunately,
the lack of a compatible equivalent for CP/M systems rendered a large amount
of public domain software inaccessible to 8-bit users such as myself. (Note
that 16-bit software can indeed be of interest to users of 8-bit systems, e.g.
Pascal and C language programs.)
Also, an increasing number of RCP/M systems now cater to both 8-bit and 16-bit
users. Since the release of UNARC 1.0 (May 3, 1986), I have been encouraged
to see that the program has found a welcome home on many such systems.
Special thanks are due to Irv Hoff and Norman Beeler for providing archive
file support in the KMD20 and LUX52 series of programs, respectively. With
the increasing popularity of .ARC files on many different computer systems, I
believe that continued such support of this compression format is both
desirable and inevitable for CP/M systems. At the time of this writing I am
about to release NOAH, a companion program to UNARC which will allow CP/M
users to generate ARC-compatible files.
Bob Freed
March 27, 1987
NOTICE
The UNARC program and its associated documentation is the copy-
righted property of its author -- it is NOT in the public domain.
HOWEVER... Free use, distribution, and modification of these
files is permitted (and encouraged), subject to the following
conditions:
(1) Such use or distribution must be for non-profit purposes only.
(2) The author's copyright notice may not be altered or removed.
(3) Modifications to this program or its documentation files may
not be distributed without notification of and approval by
the author.
(4) The source program code may not be used, in whole or in part,
in any other publicly-distributed or derivative work without
similar notification and approval.
No fee is requested or expected for the use and distribution of
this program subject to the above conditions. The author reserves
the right to modify these conditions for any future revisions of
this program. Questions, comments, suggestions, commercial
inquiries, and bug reports or fixes are welcomed by the author:
Robert A. Freed
62 Miller Road
Newton Centre, MA 02159
Telephone (617) 332-3533
------------------------------------------------------------------------------

For additional information about archive files and the MS-DOS ARC utility,
refer to the documentation file, ARC.DOC, which is available on the web.
For additional information about the LZW algorithm (and data compression
methods in general), refer to the article "A Technique for High-Performance
Data Compression", by Terry A. Welch, in IEEE Computer magazine, Vol. 17,
No. 6, June 1984.


Binary file not shown.

View File

@@ -1,42 +1,19 @@
#
# order is actually important, because of build dependencies
#
BUILDPROP := 1
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
# Inhibit building Propeller on unsupported
# Linux host architectures such as ARM eg. Raspberry Pi
BUILDPROP := 0
ARCH := $(shell uname -m)
ifeq ($(ARCH), x86_64)
BUILDPROP := 1
endif
ifeq ($(ARCH), i686)
BUILDPROP := 1
endif
ifeq ($(ARCH), i386)
BUILDPROP := 1
endif
ifeq ($(ARCH), x86)
BUILDPROP := 1
endif
# Add here any more supported x86 based
# uname machine strings for building Propeller
endif
.PHONY: doc prop shared bp images rom zrc zzrcc
.PHONY: doc prop shared bp images rom zrc z1rcc zzrcc
all: prop shared images rom zrc zzrcc
.ONESHELL:
.SHELLFLAGS = -cex
all: prop shared images rom zrc z1rcc zzrcc
doc:
$(MAKE) --directory Doc $(ACTION)
prop:
ifeq ($(BUILDPROP), 1)
$(MAKE) --directory Prop $(ACTION)
else
$(info Builing Propeller is not supported on this $(ARCH) host Linux OS)
endif
shared:
$(MAKE) --directory HDIAG $(ACTION)
@@ -67,14 +44,14 @@ rom:
zrc:
$(MAKE) --directory ZRC $(ACTION)
z1rcc:
$(MAKE) --directory Z1RCC $(ACTION)
zzrcc:
$(MAKE) --directory ZZRCC $(ACTION)
clean: ACTION=clean
clean: all
diff: ACTION=diff
diff: all

View File

@@ -3,17 +3,17 @@ setlocal
set TOOLS=../../Tools
set PATH=%TOOLS%\bst;%PATH%
set PATH=%TOOLS%\OpenSpin;%PATH%
call :bstc PropIO
call :bstc PropIO2
call :bstc ParPortProp
call :openspin PropIO
call :openspin PropIO2
call :openspin ParPortProp
goto :eof
:bstc
:openspin
echo.
echo Building %1...
bstc Spin\%1 -e -l || exit /b
move /Y %1.eeprom "..\..\Binary" || exit /b
openspin -e Spin\%1.spin || exit /b
move /Y Spin\%1.eeprom "..\..\Binary" || exit /b
goto :eof

View File

@@ -1,5 +1,5 @@
@echo off
setlocal
if exist *.eeprom del *.eeprom
if exist *.list del *.list
if exist Spin\*.eeprom del Spin\*.eeprom
if exist Spin\*.list del Spin\*.list

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,20 @@
Z1RCC has no real ROM. It has a single 512K RAM chip. The RomWBW
ROMless startup mode is used.
The ROMless startup mode treats the entire 512KB as RAM. The entire
512KB of RAM (less the top 32KB) must be preloaded by the Z1RCC CF
Loader. There will be no ROM disk available under RomWBW. There will
be a RAM Disk and it's initial contents will be seeded by the image
loaded by the CF Loader.
Bank Contents Description
-------- -------- -----------
0x0 BIOS HBIOS Bank (operating)
0x1 IMG0 ROM Loader, Monitor, ROM OSes
0x2 IMG1 ROM Applications
0x3 IMG2 Reserved
0x4-0xB RAMD RAM Disk Banks
0xC BUF OS Buffers (CP/M3)
0xD AUX Aux Bank (CP/M 3, BPBIOS, etc.)
0xE USR User Bank (CP/M TPA, etc.)
0xF COM Common Bank, Upper 32KB

23
Source/Z1RCC/Build.cmd Normal file
View File

@@ -0,0 +1,23 @@
@echo off
setlocal
set TOOLS=../../Tools
set PATH=%TOOLS%\srecord;%PATH%
if exist ..\..\Binary\RCZ180_z1rcc.rom call :build_z1rcc
goto :eof
:build_z1rcc
srec_cat -generate 0x0 0x100000 --constant 0x00 -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x0 0x200 z1rcc_cfldr.bin -binary -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x1B8 0x200 z1rcc_ptbl.bin -binary -offset 0x1B8 -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x1F000 0x20000 z1rcc_mon.bin -binary -offset 0x1F000 -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x24000 0xA4000 ..\..\Binary\RCZ180_z1rcc.rom -binary -offset 0x24000 -o temp.dat -binary
move temp.dat ..\..\Binary\hd1k_z1rcc_prefix.dat
copy /b ..\..\Binary\hd1k_z1rcc_prefix.dat + ..\..\Binary\hd1k_cpm22.img + ..\..\Binary\hd1k_zsdos.img + ..\..\Binary\hd1k_nzcom.img + ..\..\Binary\hd1k_cpm3.img + ..\..\Binary\hd1k_zpm3.img + ..\..\Binary\hd1k_ws4.img ..\..\Binary\hd1k_z1rcc_combo.img || exit /b
goto :eof

3
Source/Z1RCC/Clean.cmd Normal file
View File

@@ -0,0 +1,3 @@
@echo off
setlocal

30
Source/Z1RCC/Makefile Normal file
View File

@@ -0,0 +1,30 @@
HD1KZ1RCCPREFIX = hd1k_z1rcc_prefix.dat
HD1KZ1RCCCOMBOIMG = hd1k_z1rcc_combo.img
Z1RCCROM = ../../Binary/RCZ180_z1rcc.rom
HD1KIMGS = ../../Binary/hd1k_cpm22.img ../../Binary/hd1k_zsdos.img ../../Binary/hd1k_nzcom.img \
../../Binary/hd1k_cpm3.img ../../Binary/hd1k_zpm3.img ../../Binary/hd1k_ws4.img
OBJECTS :=
ifneq ($(wildcard $(Z1RCCROM)),)
OBJECTS += $(HD1KZ1RCCPREFIX) $(HD1KZ1RCCCOMBOIMG)
endif
DEST=../../Binary
TOOLS = ../../Tools
include $(TOOLS)/Makefile.inc
DIFFPATH = $(DIFFTO)/Binary
$(HD1KZ1RCCPREFIX):
srec_cat -generate 0x0 0x100000 --constant 0x00 -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x0 0x200 z1rcc_cfldr.bin -binary -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x1B8 0x200 z1rcc_ptbl.bin -binary -offset 0x1B8 -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x1F000 0x20000 z1rcc_mon.bin -binary -offset 0x1F000 -o temp.dat -binary
srec_cat temp.dat -binary -exclude 0x24000 0xA4000 $(Z1RCCROM) -binary -offset 0x24000 -o temp.dat -binary
mv temp.dat $@
$(HD1KZ1RCCCOMBOIMG): $(HD1KZ1RCCPREFIX) $(HD1KIMGS)
cat $^ > $@

View File

@@ -0,0 +1,24 @@
Z1RCC Disk Prefix Layout
======================
---- Bytes ---- --- Sectors ---
Start Length Start Length Description
------- ------- ------- ------- ---------------------------
0x00000 0x001BE 0 1 CF Boot Loader
0x001B8 0x00048 RomWBW Partition Table
0x00200 0x1EE00 1 247 Unused
0x1F000 0x01000 248 8 Z1RCC Monitor v0.2a
0x20000 0x04000 256 32 Unused
0x24000 0x80000 288 1024 RomWBW
0xA4000 0x5C000 1312 736 Unused
0x100000 2048 Start of slices (partition 0x1E)
Notes
-----
- At startup CPLD ROM is mapped to Z80 CPU address space 0x0000-0x003F, CPU begins execution at 0x0000
- CPLD ROM (CF bootstrap mode) reads CF Boot Loader (512B) from start of CF (MBR) to 0xA000 and runs it
- CF Boot Loader reads Z1RCC Monitor (4KB) from sectors 0xF8-0xFF of CF to 0xB000 and runs from 0xB400
- Z1RCC Monitor reads 480KB (RomWBW) from sectors 0x120-0x4DF of CF into 480KB of physical RAM

Binary file not shown.

BIN
Source/Z1RCC/z1rcc_mon.bin Normal file

Binary file not shown.

BIN
Source/Z1RCC/z1rcc_ptbl.bin Normal file

Binary file not shown.

View File

@@ -2,7 +2,7 @@
#DEFINE RMN 4
#DEFINE RUP 0
#DEFINE RTP 0
#DEFINE BIOSVER "3.4.0-dev.10"
#DEFINE BIOSVER "3.4.0-dev.22"
#define rmj RMJ
#define rmn RMN
#define rup RUP

View File

@@ -3,5 +3,5 @@ rmn equ 4
rup equ 0
rtp equ 0
biosver macro
db "3.4.0-dev.10"
db "3.4.0-dev.22"
endm

View File

@@ -1,6 +1,10 @@
#
# build the tools for linux and Darwin
#
.ONESHELL:
.SHELLFLAGS = -cex
UNAME := $(shell uname)
all:

View File

@@ -42,7 +42,6 @@ export CPMDIR80=$(TOOLS)/cpm/
TASM=$(BINDIR)/uz80as -t z80
TASMFLAGS=
OPENSPIN=$(BINDIR)/openspin
BSTC=$(BINDIR)//bstc
CPMCP=$(BINDIR)/cpmcp
CPMCHATTR=$(BINDIR)/cpmchattr
@@ -115,19 +114,8 @@ CPM=$(TOOLS)/cpm/bin80/
%.com: %.rel
$(ZXCC) $(CPM)/L80 -$(basename $<),$(basename $<).com/n/e
ifeq ($(UNAME), Linux)
%.eeprom: %.spin
$(BSTC) -e -l $<
endif
#
# darwin bstc won't run, since mac os does not do 32 bit binaries any more
# openspin ought to work
#
ifeq ($(UNAME), Darwin)
%.eeprom: %.spin
$(OPENSPIN) -e $<
endif
#
# first target is default

BIN
Tools/OpenSpin/openspin.exe Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,10 @@
#
# build the tools for linux and Darwin
#
.ONESHELL:
.SHELLFLAGS = -cex
UNAME := $(shell uname)
ifeq ($(UNAME), Linux)
SUFFIX=linux
@@ -9,7 +13,7 @@ ifeq ($(UNAME), Darwin)
SUFFIX=osx
endif
SUBDIRS = bst uz80as zxcc cpmtools bin2asm lzsa
SUBDIRS = OpenSpin uz80as zxcc cpmtools bin2asm lzsa
all:
@chmod +x casefn.sh

View File

@@ -0,0 +1,5 @@
language: c
os:
- linux
- osx
script: make

View File

@@ -0,0 +1,68 @@
# modified for RomWBW build environment
UNAME := $(shell uname)
DEST = ../../$(UNAME)
# cross compilation scheme taken from Eric Smith's spin2cpp compiler
# if CROSS is defined, we are building a cross compiler
# possible targets are: win32, rpi
ifeq ($(CC),)
CC=gcc
endif
ifeq ($(CXX),)
CXX=g++
endif
ifeq ($(CROSS),win32)
CC=i686-w64-mingw32-gcc
CXX=i686-w64-mingw32-g++
EXT=.exe
BUILD=./build-win32
else ifeq ($(CROSS),rpi)
CC=arm-linux-gnueabihf-gcc
CXX=arm-linux-gnueabihf-g++
EXT=
BUILD=./build-rpi
else
EXT=
BUILD=./build
endif
OS:=$(shell uname)
ifeq ($(OS),Darwin)
CFLAGS+=-Wall -g -Wno-self-assign
else
CFLAGS+=-Wall -g $(MSTATIC)
endif
CXXFLAGS += $(CFLAGS)
TARGET=$(BUILD)/openspin$(EXT)
SRCDIR=SpinSource
OBJ=$(BUILD)/openspin.o \
$(BUILD)/pathentry.o
LIBNAME=$(BUILD)/PropellerCompiler/libopenspin.a
all: $(BUILD) $(DEST) $(LIBNAME) $(OBJ) Makefile
$(CXX) -o $(TARGET) $(CXXFLAGS) $(OBJ) $(LIBNAME)
cp -p $(TARGET) $(DEST)
$(BUILD)/%.o: $(SRCDIR)/%.cpp
$(CXX) $(CXXFLAGS) -o $@ -c $<
$(LIBNAME): $(BUILD)
$(MAKE) -C PropellerCompiler CROSS=$(CROSS) BUILD=$(realpath $(BUILD))/PropellerCompiler all
$(BUILD):
mkdir -p $(BUILD)
$(DEST):
mkdir -p $(DEST)
clean:
rm -rf $(BUILD)
make -C PropellerCompiler BUILD=$(realpath $(BUILD))/PropellerCompiler clean

View File

@@ -0,0 +1,136 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// BlockNestStackRoutines.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
#include "CompileUtilities.h"
//
// Block Nest Routines
//
bool BlockNest_New(unsigned char type, int stackSize)
{
if (g_pCompilerData->bnest_ptr > block_nest_limit)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_loxnbe];
return false;
}
// set blockstack base
g_pCompilerData->bnest_type[g_pCompilerData->bnest_ptr] = type;
g_pCompilerData->bstack_base[g_pCompilerData->bnest_ptr++] = g_pCompilerData->bstack_ptr;
// init bstack values to max forward
for (int i = 0; i < stackSize; i++)
{
g_pCompilerData->bstack[g_pCompilerData->bstack_ptr + i] = 0x0000FFC0;
}
g_pCompilerData->bstack_ptr += stackSize;
if (g_pCompilerData->bstack_ptr >= block_stack_limit)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_bnso];
return false;
}
return true;
}
void BlockNest_Redo(unsigned char type)
{
g_pCompilerData->bnest_type[g_pCompilerData->bnest_ptr - 1] = type;
}
void BlockNest_End()
{
g_pCompilerData->bnest_ptr--;
g_pCompilerData->bstack_ptr = g_pCompilerData->bstack_base[g_pCompilerData->bnest_ptr];
}
//
// Block Stack Routines
//
void BlockStack_Write(int address, int value)
{
int stackAddress = g_pCompilerData->bstack_base[g_pCompilerData->bnest_ptr - 1] + address;
g_pCompilerData->bstack[stackAddress] = value;
}
int BlockStack_Read(int address)
{
int stackAddress = g_pCompilerData->bstack_base[g_pCompilerData->bnest_ptr - 1] + address;
return g_pCompilerData->bstack[stackAddress];
}
bool BlockStack_CompileAddress(int address)
{
return CompileAddress(BlockStack_Read(address));
}
bool BlockStack_CompileConstant()
{
int value = BlockStack_Read(0);
if (value >= 0x100)
{
// two byte
if (!EnterObj(0x39)) // 0x39 = 00111001b
{
return false;
}
if (!EnterObj((unsigned char)((value >> 8) & 0xFF)))
{
return false;
}
}
else
{
// one byte
if (!EnterObj(0x38)) // 0x38 = 00111000b
{
return false;
}
}
return EnterObj((unsigned char)(value & 0xFF));
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,932 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// CompileDatBlocks.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
void CompileDatBlocks_EnterInfo(int datstart, int objstart)
{
g_pCompilerData->inf_start = datstart;
g_pCompilerData->inf_finish = g_pElementizer->GetSourcePtr();
g_pCompilerData->inf_data0 = objstart;
g_pCompilerData->inf_data1 = g_pCompilerData->obj_ptr;
g_pCompilerData->inf_data2 = 0;
g_pCompilerData->inf_data3 = 0;
g_pCompilerData->inf_data4 = 0;
g_pCompilerData->inf_type = info_dat;
EnterInfo();
}
void CompileDatBlocks_EnterSymbol(bool bResSymbol, int size)
{
int value_1 = g_pCompilerData->obj_ptr;
int value_2 = g_pCompilerData->cog_org;
g_pCompilerData->inf_data0 = value_1;
g_pCompilerData->inf_data1 = size;
g_pCompilerData->inf_data2 = value_2;
g_pCompilerData->inf_data3 = 0;
g_pCompilerData->inf_data4 = 0;
g_pCompilerData->inf_type = info_dat_symbol;
EnterInfo();
g_pSymbolEngine->AddSymbol(g_pCompilerData->symbolBackup, bResSymbol ? type_dat_long_res : (size == 0 ? type_dat_byte : (size == 1 ? type_dat_word : type_dat_long)), value_1, value_2);
#ifdef RPE_DEBUG
printf("dat: %s %08X %08X (%d)\n", g_pCompilerData->symbolBackup, value_1, value_2, size);
#endif
}
bool CompileDatBlocks_EnterByte(unsigned char value)
{
if (EnterObj(value))
{
if (g_pCompilerData->orgx == 0)
{
g_pCompilerData->cog_org++;
}
return true;
}
return false;
}
bool CompileDatBlocks_Enter(int value, int count, int size)
{
int numBytesPer = 1 << size;
for (int i = 0; i < count; i++)
{
if(!CompileDatBlocks_EnterByte(value & 0x000000FF))
{
return false;
}
if (numBytesPer > 1)
{
if(!CompileDatBlocks_EnterByte((value >> 8) & 0x000000FF))
{
return false;
}
}
if (numBytesPer > 2)
{
if(!CompileDatBlocks_EnterByte((value >> 16) & 0x000000FF))
{
return false;
}
if(!CompileDatBlocks_EnterByte((value >> 24) & 0x000000FF))
{
return false;
}
}
}
return true;
}
bool CompileDatBlocks_Advance(bool bSymbol, bool bResSymbol, int size)
{
int testVal = (1 << size) - 1;
for (;;)
{
if ((g_pCompilerData->obj_ptr & testVal) == 0)
{
if (bSymbol)
{
CompileDatBlocks_EnterSymbol(bResSymbol, size);
}
break;
}
if (!CompileDatBlocks_EnterByte(0)) // obj_ptr gets incremented in here
{
return false;
}
}
return true;
}
bool CompileDatBlocks_Data(bool& bEof, int pass, bool bSymbol, bool& bResSymbol, int& size)
{
size = g_pElementizer->GetValue() & 0x000000FF;
int overrideSize = size;
if (!CompileDatBlocks_Advance(bSymbol, bResSymbol, size))
{
return false;
}
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_end)
{
return true;
}
while (!bEof)
{
// do we have a size override?
if (g_pElementizer->GetType() == type_size)
{
// yes, get it
overrideSize = g_pElementizer->GetValue() & 0x000000FF;
if (overrideSize < size)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_sombl];
return false;
}
}
else
{
// no, backup
g_pElementizer->Backup();
}
// get the value
if (!GetTryValue(pass == 1 ? true : false, overrideSize == 2 ? false : true, true))
{
return false;
}
int value = GetResult();
// get the count
int count = 1;
if (g_pElementizer->CheckElement(type_leftb))
{
if (!GetTryValue(true, true, true))
{
return false;
}
count = GetResult();
if (!g_pElementizer->GetElement(type_rightb))
{
return false;
}
}
// enter the value count times into the obj
if (!CompileDatBlocks_Enter(value, count, overrideSize))
{
return false;
}
bool bComma = false;
if (!GetCommaOrEnd(bComma))
{
return false;
}
if (!bComma)
{
break;
}
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
}
return true;
}
bool CompileDatBlocks_File(bool bSymbol, bool bResSymbol, int& size)
{
size = 0; // force size to byte
if (bSymbol)
{
CompileDatBlocks_EnterSymbol(bResSymbol, size);
}
int filenameStart = 0;
int filenameFinish = 0;
if (!GetFilename(filenameStart, filenameFinish))
{
return false;
}
// find the file in the dat_data array and copy it into obj
for (int i = 0; i < g_pCompilerData->dat_files; i++)
{
if (strcmp(&(g_pCompilerData->dat_filenames[256*i]), g_pCompilerData->filename) == 0)
{
// copy dat data into obj (RPE: this should be optimized)
for (int j = 0; j < g_pCompilerData->dat_lengths[i]; j++)
{
if (!CompileDatBlocks_EnterByte(g_pCompilerData->dat_data[g_pCompilerData->dat_offsets[i] + j]))
{
return false;
}
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
return true;
}
}
// file data not found
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_idfnf];
return false;
}
bool CompileDatBlocks_AsmDirective(bool bSymbol, bool& bResSymbol, int& size)
{
size = 2; // force to long size
int directive = g_pElementizer->GetValue() & 0x000000FF;
switch (directive)
{
case dir_nop:
{
if (!CompileDatBlocks_Advance(bSymbol, bResSymbol, size))
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
if (!CompileDatBlocks_Enter(0, 1, 2)) // enter a 0 long
{
return false;
}
return true;
}
break;
case dir_fit:
{
if (!CompileDatBlocks_Advance(bSymbol, bResSymbol, size))
{
return false;
}
int fit = 0x1F0;
if (!g_pElementizer->CheckElement(type_end))
{
if (!GetTryValue(true, true, true))
{
return false;
}
fit = GetResult();
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
}
fit <<= 2;
if ((unsigned int)(g_pCompilerData->cog_org) > (unsigned int)fit)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_oefl];
return false;
}
return true;
}
break;
case dir_res:
{
if (g_pCompilerData->orgx != 0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_rinaiom];
return false;
}
bResSymbol = true;
if (!CompileDatBlocks_Advance(bSymbol, bResSymbol, size))
{
return false;
}
int resSize = 1;
if (!g_pElementizer->CheckElement(type_end))
{
if (!GetTryValue(true, true, true))
{
return false;
}
resSize = GetResult();
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
}
resSize <<= 2;
g_pCompilerData->cog_org += resSize;
if (g_pCompilerData->cog_org > (0x1F0 * 4))
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_oexl];
return false;
}
return true;
}
break;
case dir_org:
{
if (!CompileDatBlocks_Advance(bSymbol, bResSymbol, size))
{
return false;
}
int newOrg = 0;
if (!g_pElementizer->CheckElement(type_end))
{
if (!GetTryValue(true, true, true))
{
return false;
}
newOrg = GetResult();
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
}
if (newOrg > 0x1F0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_oexl];
return false;
}
g_pCompilerData->cog_org = newOrg << 2;
g_pCompilerData->orgx = 0;
return true;
}
break;
}
if (!CompileDatBlocks_Advance(bSymbol, bResSymbol, size))
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
g_pCompilerData->cog_org = 0;
g_pCompilerData->orgx = 1;
return true;
}
bool CompileDatBlocks_ValidateCallSymbol(bool bIsRet, char* pSymbol)
{
if (!g_pElementizer->FindSymbol(pSymbol))
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eads];
return false;
}
if (g_pElementizer->GetType() == type_undefined)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[bIsRet ? error_urs : error_us];
return false;
}
if (g_pElementizer->GetType() < type_dat_byte || g_pElementizer->GetType() > type_dat_long_res)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eads];
return false;
}
// the offset to the label symbol is in second symbol value
int value = g_pElementizer->GetValue2();
// make sure it's long aligned
if (value & 0x03)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[bIsRet ? error_rainl : error_ainl];
return false;
}
// make sure is in range
value >>= 2;
if (value >= 0x1F0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[bIsRet ? error_raioor : error_aioor];
return false;
}
return true;
}
bool CompileDatBlocks_AsmInstruction(bool& bEof, int pass, bool bSymbol, bool bResSymbol, int& size, unsigned char condition)
{
size = 2; // force to long size
if (!CompileDatBlocks_Advance(bSymbol, bResSymbol, size))
{
return false;
}
int opcode = g_pElementizer->GetValue() & 0x000000FF;
// handle dual type entries and also AND and OR (which are the only type_binary that will get here)
if (g_pElementizer->IsDual() || g_pElementizer->GetType() == type_binary)
{
opcode = g_pElementizer->GetAsm() & 0x000000FF;
}
unsigned int instruction = opcode << 8;
if (opcode & 0x80) // sys instruction
{
instruction = 0x03 << 8;
}
instruction |= condition;
if (opcode & 0x40) // set WR?
{
instruction |= 0x20;
}
instruction <<= 18; // justify the instruction (s & d will go in lower 18 bits)
if (opcode & 0x80) // sys instruction
{
instruction |= 0x00400000; // set immediate
instruction |= (opcode & 0x07); // set s
// get d
if (!GetTryValue(pass == 1 ? true : false, true, true))
{
return false;
}
int d = GetResult();
if (d > 0x1FF)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_drcex];
return false;
}
instruction |= (d << 9); // set d
}
else if (opcode == 0x15) // call?
{
// make 'jmpret label_ret, #label'
instruction ^= 0x08C00000;
if (!g_pElementizer->GetElement(type_pound))
{
return false;
}
int length = 0;
if (!GetSymbol(&length))
{
return false;
}
if (length > 0)
{
if (length > symbol_limit - 4)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_csmnexc];
return false;
}
char* pSymbol = g_pElementizer->GetCurrentSymbol();
if (pass == 1)
{
if (!CompileDatBlocks_ValidateCallSymbol(false, pSymbol))
{
return false;
}
}
instruction |= ((g_pElementizer->GetValue2() & 0x7FF) >> 2); // set #label
pSymbol[length] = '_';
pSymbol[length+1] = 'R';
pSymbol[length+2] = 'E';
pSymbol[length+3] = 'T';
pSymbol[length+4] = 0;
if (pass == 1)
{
if (!CompileDatBlocks_ValidateCallSymbol(true, pSymbol))
{
return false;
}
}
instruction |= (((g_pElementizer->GetValue2() & 0x7FF) >> 2) << 9); // set label_ret
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eads];
return false;
}
}
else if (opcode == 0x16) // ret?
{
instruction ^= 0x04400000; // make 'jmp #0'
}
else if (opcode == 0x17) // jmp?
{
// for jmp, we only get s, there is no d
// see if it's an immediate value for s
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_pound)
{
instruction |= 0x00400000;
}
else
{
g_pElementizer->Backup();
}
// get s
if (!GetTryValue(pass == 1 ? true : false, true, true))
{
return false;
}
int s = GetResult();
// make sure it's in range
if (s > 0x1FF)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_srccex];
return false;
}
// set s on instruction
instruction |= s;
}
else // regular instruction get both d and s
{
// get d
if (!GetTryValue(pass == 1 ? true : false, true, true))
{
return false;
}
int d = GetResult();
// make sure it's in range
if (d > 0x1FF)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_drcex];
return false;
}
// set d on instruction
instruction |= (d << 9);
if (!g_pElementizer->GetElement(type_comma))
{
return false;
}
// see if it's an immediate value for s
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_pound)
{
instruction |= 0x00400000;
}
else
{
g_pElementizer->Backup();
}
// get s
if (!GetTryValue(pass == 1 ? true : false, true, true))
{
return false;
}
int s = GetResult();
// make sure it's in range
if (s > 0x1FF)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_srccex];
return false;
}
// set s on instruction
instruction |= s;
}
// check for effects
bool bAfterComma = false;
while (!bEof)
{
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_asm_effect)
{
int effectValue = g_pElementizer->GetValue();
// don't allow wr/nr for r/w instructions
if ((effectValue & 0x09) && (instruction >> 26) <= 2)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_micuwn];
return false;
}
// apply effect to instruction
int temp = (effectValue & 0x38) << 20;
instruction |= temp;
instruction ^= temp;
instruction |= ((effectValue & 0x07) << 23);
bool bComma = false;
if (!GetCommaOrEnd(bComma))
{
return false;
}
if (!bComma)
{
// got end, done with effects
break;
}
// got a comma, expecting another effect
bAfterComma = true;
}
else if (bAfterComma)
{
// expected another effect after the comma
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eaasme];
return false;
}
else if (g_pElementizer->GetType() != type_end)
{
// if it wasn't an effect the first time in then it should be an end
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eaaeoeol];
return false;
}
else
{
// we get here if we got no effect and got the proper end
break;
}
}
// enter instruction as 1 long
if (!CompileDatBlocks_Enter(instruction, 1, 2))
{
return false;
}
return true;
}
bool CompileDatBlocks_CheckInstruction()
{
if (g_pElementizer->GetType() == type_asm_inst || g_pElementizer->IsDual())
{
return true;
}
if (g_pElementizer->GetType() == type_binary)
{
if (g_pElementizer->GetOpType() == op_log_and || g_pElementizer->GetOpType() == op_log_or)
{
return true;
}
}
return false;
}
bool CompileDatBlocks_AsmCondition(bool& bEof, int pass, bool bSymbol, bool bResSymbol, int& size)
{
unsigned char condition = (unsigned char)(g_pElementizer->GetValue() & 0x000000FF);
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (CompileDatBlocks_CheckInstruction())
{
return CompileDatBlocks_AsmInstruction(bEof, pass, bSymbol, bResSymbol, size, condition);
}
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eaasmi];
return false;
}
bool CompileDatBlocks()
{
int infoflag = 0;
int ptr = g_pCompilerData->obj_ptr;
int datstart = 0;
int objstart = 0;
for (int pass = 0; pass < 2; pass++)
{
g_pCompilerData->obj_ptr = ptr;
g_pCompilerData->asm_local = 0;
g_pCompilerData->cog_org = 0;
g_pCompilerData->orgx = 0;
int size = 0;
bool bEof = false;
g_pElementizer->Reset();
while(!bEof)
{
if(g_pElementizer->GetNextBlock(block_dat, bEof))
{
if (bEof)
{
break;
}
datstart = g_pCompilerData->source_start;
objstart = g_pCompilerData->obj_ptr;
while (!bEof)
{
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (bEof)
{
break;
}
infoflag = 1;
if (g_pElementizer->GetType() == type_end)
{
continue;
}
g_pCompilerData->inf_start = g_pCompilerData->source_start;
// clear symbol flags
bool bLocal = false;
bool bSymbol = false;
bool bResSymbol = false;
if (!CheckLocal(bLocal)) // bLocal will be set if it is a local
{
return false;
}
g_pCompilerData->inf_finish = g_pCompilerData->source_finish;
if (g_pElementizer->GetType() == type_undefined) // undefined here means it's a symbol
{
if (!bLocal)
{
if (!IncrementAsmLocal())
{
return false;
}
}
bSymbol = true;
g_pElementizer->BackupSymbol();
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_end)
{
if (bSymbol)
{
CompileDatBlocks_EnterSymbol(bResSymbol, size);
}
continue;
}
}
else if (g_pElementizer->GetType() == type_dat_byte ||
g_pElementizer->GetType() == type_dat_word ||
g_pElementizer->GetType() == type_dat_long ||
g_pElementizer->GetType() == type_dat_long_res)
{
if (!bLocal)
{
if (!IncrementAsmLocal())
{
return false;
}
}
if (pass == 0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_siad];
return false;
}
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_end)
{
continue;
}
}
if (g_pElementizer->GetType() == type_size)
{
if (!CompileDatBlocks_Data(bEof, pass, bSymbol, bResSymbol, size))
{
return false;
}
continue;
}
else if (g_pElementizer->GetType() == type_file)
{
if (!CompileDatBlocks_File(bSymbol, bResSymbol, size))
{
return false;
}
continue;
}
else if (g_pElementizer->GetType() == type_asm_dir)
{
if (!CompileDatBlocks_AsmDirective(bSymbol, bResSymbol, size))
{
return false;
}
continue;
}
else if (g_pElementizer->GetType() == type_asm_cond)
{
if (!CompileDatBlocks_AsmCondition(bEof, pass, bSymbol, bResSymbol, size))
{
return false;
}
continue;
}
else if (CompileDatBlocks_CheckInstruction())
{
if (!CompileDatBlocks_AsmInstruction(bEof, pass, bSymbol, bResSymbol, size, if_always))
{
return false;
}
continue;
}
if (g_pElementizer->GetType() == type_block)
{
g_pElementizer->Backup();
if (pass == 0)
{
CompileDatBlocks_EnterInfo(datstart, objstart);
}
break;
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eaunbwlo];
return false;
}
}
}
else
{
return false;
}
}
if (infoflag != 0 && pass == 0)
{
CompileDatBlocks_EnterInfo(datstart, objstart);
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,755 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// CompileExpression.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
#include "CompileUtilities.h"
//
//************************************************************************
//* Expression Compiler *
//************************************************************************
//
// Basic expression syntax rules: i.e. 4000/(||x*5)//127)+1
//
// Any one of these... Must be followed by any one of these...
// ------------------------------------------------------------------
// term binary operator
// ) )
// <end>
//
// Any one of these... Must be followed by any one of these... *
// ------------------------------------------------------------------
// unary operator term
// binary operator unary operator
// ( (
//
// * initial element of an expression
//
// forward declarations
bool CompileTerm();
bool CompileSubExpression(int precedence);
bool CompileTopExpression();
// Compile expression with sub-expressions
bool CompileExpression()
{
if (!CompileTopExpression())
{
return false;
}
return true;
}
bool CompileTopExpression()
{
if (!CompileSubExpression(11))
{
return false;
}
return true;
}
bool CompileSubExpression_Term()
{
// get next element ignoring any leading +'s
bool bEof = false;
do
{
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
} while (g_pElementizer->GetType() == type_binary && g_pElementizer->GetOpType() == op_add);
if (!g_pElementizer->NegConToCon())
{
return false;
}
g_pElementizer->SubToNeg();
int opType = g_pElementizer->GetOpType();
switch (g_pElementizer->GetType())
{
case type_atat:
if (!CompileSubExpression(0))
{
return false;
}
if (!EnterObj(0x97)) // memop byte+index+pbase+address
{
return false;
}
if (!EnterObj(0)) // address 0
{
return false;
}
break;
case type_unary:
if (!CompileSubExpression(g_pElementizer->GetValue())) // value = precedence for type_unary
{
return false;
}
if (!EnterObj((unsigned char)opType | 0xE0)) // math
{
return false;
}
break;
case type_left:
if (!CompileTopExpression())
{
return false;
}
if (!g_pElementizer->GetElement(type_right))
{
return false;
}
break;
default:
if (!CompileTerm())
{
return false;
}
break;
}
return true;
}
bool CompileSubExpression(int precedence)
{
precedence--;
if (precedence < 0)
{
if (!CompileSubExpression_Term())
{
return false;
}
return true;
}
else
{
if (!CompileSubExpression(precedence))
{
return false;
}
}
for (;;)
{
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() != type_binary)
{
g_pElementizer->Backup();
break;
}
// if we got here then it's type_binary (so the value is the precedence)
if (g_pElementizer->GetValue() != precedence)
{
g_pElementizer->Backup();
break;
}
int opType = g_pElementizer->GetOpType();
if (!CompileSubExpression(precedence))
{
return false;
}
if (!EnterObj((unsigned char)(opType | 0xE0)))
{
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////
//
// CompileTerm functions
//
// compile constant(constantexpression)
bool CompileTerm_ConExp()
{
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
if (!GetTryValue(true, false))
{
return false;
}
if (!CompileConstant(GetResult()))
{
return false;
}
return g_pElementizer->GetElement(type_right);
}
// compile string("constantstring")
bool CompileTerm_ConStr()
{
if (g_pCompilerData->str_enable == false)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_snah];
return false;
}
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
if (!StringConstant_GetIndex()) // get index in g_pCompilerData->str_index
{
return false;
}
// get the string into the string constant buffer
for (;;)
{
if (!GetTryValue(true, false))
{
return false;
}
int value = GetResult();
if (g_pCompilerData->intMode == 2 || value == 0 || value > 0xFF)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_scmr];
return false;
}
if (!StringConstant_EnterChar((unsigned char)(value & 0xFF))) // add character to string constant buffer
{
return false;
}
// more characters?
bool bComma = false;
if (!GetCommaOrRight(bComma))
{
return false;
}
if (!bComma)
{
// got right ')'
break;
}
}
StringConstant_EnterChar(0); // enter 0 terminator into string constant buffer
if (!EnterObj(0x87)) // (memcp byte+pbase+address)
{
return false;
}
StringConstant_EnterPatch(); // enter string constant patch address
// enter two address bytes (patched later)
if (!EnterObj(0x80))
{
return false;
}
return EnterObj(0);
}
// compile float(integer)/round(float)/trunc(float)
bool CompileTerm_FloatRoundTrunc()
{
g_pElementizer->Backup(); // backup to float/round/trunc
if (!GetTryValue(true, false))
{
return false;
}
return CompileConstant(GetResult());
}
bool CompileTerm_Sub(unsigned char anchor, int value)
{
if (!EnterObj(anchor)) // drop anchor
{
return false;
}
if (!CompileParameters((value & 0x0000FF00) >> 8))
{
return false;
}
if (!EnterObj(0x05)) // call sub
{
return false;
}
return EnterObj((unsigned char)(value & 0xFF)); // index of sub
}
// compile obj[].pub
bool CompileTerm_ObjPub(unsigned char anchor, int value)
{
if (!EnterObj(anchor)) // drop anchor
{
return false;
}
// check for [index]
bool bIndex = false;
int expSourcePtr = 0;
if (!CheckIndex(bIndex, expSourcePtr))
{
return false;
}
if (!g_pElementizer->GetElement(type_dot))
{
return false;
}
// lookup the pub symbol
if (!GetObjSymbol(type_objpub, (char)((value & 0x0000FF00) >> 8)))
{
return false;
}
int objPubValue = g_pElementizer->GetValue();
// compile any parameters the pub has
if (!CompileParameters((objPubValue & 0x0000FF00) >> 8))
{
return false;
}
unsigned char byteCode = 0x06; // call obj.pub
if (bIndex)
{
if (!CompileOutOfSequenceExpression(expSourcePtr))
{
return false;
}
byteCode = 0x07; // call obj[].pub
}
if (!EnterObj(byteCode))
{
return false;
}
if (!EnterObj((unsigned char)(value & 0xFF))) // index of obj
{
return false;
}
return EnterObj((unsigned char)(objPubValue & 0xFF)); // index of objpub
}
// compile obj[].pub\obj[]#con
bool CompileTerm_ObjPubCon(int value)
{
if (!g_pElementizer->CheckElement(type_pound)) // check for obj#con
{
// not obj#con, so do obj[].pub
return CompileTerm_ObjPub(0, value);
}
// lookup the symbol to get the value to compile
if (!GetObjSymbol(type_objcon, (char)((value & 0x0000FF00) >> 8)))
{
return false;
}
return CompileConstant(g_pElementizer->GetValue());
}
// compile \sub or \obj
bool CompileTerm_Try(unsigned char anchor)
{
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_sub)
{
return CompileTerm_Sub(anchor, g_pElementizer->GetValue());
}
else if (g_pElementizer->GetType() == type_obj)
{
return CompileTerm_ObjPub(anchor, g_pElementizer->GetValue());
}
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_easoon];
return false;
}
bool CompileLook(int column, int param)
{
column = column; // stop warning
param &= 0xFF; // we only care about the bottom byte
unsigned char byteCode = 0x35; // constant 0
if (param < 0x80) // zero based?
{
byteCode += 1; // not, so make it a constant 1
}
if (!EnterObj(byteCode))
{
return false;
}
if (!BlockStack_CompileConstant()) // enter address constant
{
return false;
}
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
if (!CompileExpression()) // compile primary value
{
return false;
}
if (!g_pElementizer->GetElement(type_colon))
{
return false;
}
for (;;)
{
bool bRange = false;
if (!CompileRange(bRange)) // compile (next) value/range
{
return false;
}
byteCode = (unsigned char)param;
if (bRange)
{
byteCode |= 2;
}
if (!EnterObj(byteCode & 0x7F))
{
return false;
}
bool bComma = false;
if (!GetCommaOrRight(bComma))
{
return false;
}
if (!bComma)
{
break;
}
}
if (!EnterObj(0x0F)) // lookdone
{
return false;
}
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set address
return true;
}
// compile 'lookup'/'lookdown'
// this one compiles like a block (see InstructionBlockCompiler.cpp stuff)
bool CompileTerm_Look(int value)
{
if (!BlockNest_New(type_i_look, 1))
{
return false;
}
if (!OptimizeBlock(0, value, &CompileLook))
{
return false;
}
BlockNest_End();
return true;
}
bool CompileTerm_ClkMode()
{
if (!EnterObj(0x38)) // constant 4
{
return false;
}
if (!EnterObj(4))
{
return false;
}
return EnterObj(0x80); // read byte[]
}
bool CompileTerm_ClkFreq()
{
if (!EnterObj(0x35)) // constant 0
{
return false;
}
return EnterObj(0xC0); // read long[]
}
bool CompileTerm_ChipVer()
{
if (!EnterObj(0x34)) // constant -1
{
return false;
}
return EnterObj(0x80); // read byte[]
}
bool CompileTerm_CogId()
{
if (!EnterObj(0x3F)) // reg op
{
return false;
}
return EnterObj(0x89); // read id
}
bool CompileTerm_Inst(int value)
{
if (!CompileParameters((value & 0xFF) >> 6))
{
return false;
}
return EnterObj((unsigned char)(value & 0x3F)); // instruction
}
bool CompileTerm_CogNew(int value)
{
// see if first param is a sub
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_sub)
{
int subConstant = g_pElementizer->GetValue();
if (!g_pCompilerData->bFinalCompile && g_pCompilerData->bUnusedMethodElimination)
{
AddCogNewOrInit(g_pCompilerData->current_filename, subConstant & 0x000000FF);
}
// it is a sub, so compile as cognew(subname(params),stack)
if (!CompileParameters((g_pElementizer->GetValue() & 0x0000FF00) >> 8))
{
return false;
}
if (!CompileConstant(subConstant))
{
return false;
}
if (!g_pElementizer->GetElement(type_comma))
{
return false;
}
if (!CompileExpression()) // compile stack expression
{
return false;
}
if (!g_pElementizer->GetElement(type_right))
{
return false;
}
if (!EnterObj(0x15)) // run
{
return false;
}
return EnterObj((unsigned char)(value & 0x3F)); // coginit
}
// it is not a sub, so backup and compile as cognew(address, parameter)
g_pElementizer->Backup();
g_pElementizer->Backup();
if (!EnterObj(0x34)) // constant -1
{
return false;
}
return CompileTerm_Inst(value);
}
// compile @var
bool CompileTerm_At()
{
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
if (!GetVariable(varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
if (varType == type_reg || varType == type_spr)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eamvaa];
return false;
}
return CompileVariable(3, 0, varType, varSize, varAddress, varIndexSourcePtr);
}
bool CompileTerm()
{
int type = g_pElementizer->GetType();
int value = g_pElementizer->GetValue();
switch(type)
{
case type_con:
case type_con_float:
return CompileConstant(value);
case type_conexp:
return CompileTerm_ConExp();
case type_constr:
return CompileTerm_ConStr();
case type_float:
case type_round:
case type_trunc:
return CompileTerm_FloatRoundTrunc();
case type_back:
return CompileTerm_Try(0x02);
case type_sub:
return CompileTerm_Sub(0, value);
case type_obj:
return CompileTerm_ObjPubCon(value);
case type_i_look:
return CompileTerm_Look(value);
case type_i_clkmode:
return CompileTerm_ClkMode();
case type_i_clkfreq:
return CompileTerm_ClkFreq();
case type_i_chipver:
return CompileTerm_ChipVer();
case type_i_cogid:
return CompileTerm_CogId();
case type_i_cognew:
return CompileTerm_CogNew(value);
case type_i_ar: // instruction always-returns
case type_i_cr: // instruction can-return
return CompileTerm_Inst(value);
case type_at: // @var
return CompileTerm_At();
case type_inc: // assign pre-inc w/push ++var
return CompileVariable_PreIncOrDec(0xA0);
case type_dec: // assign pre-dec w/push --var
return CompileVariable_PreIncOrDec(0xB0);
case type_til: // assign sign-extern byte w/push ~var
return CompileVariable_PreSignExtendOrRandom(0x90);
case type_tiltil: // assign sign-extern word w/push ~~var
return CompileVariable_PreSignExtendOrRandom(0x94);
case type_rnd: // assign random forward w/push ?var
return CompileVariable_PreSignExtendOrRandom(0x88);
}
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
bool bVariable = false;
if (!CheckVariable(bVariable, varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
if (!bVariable)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eaet];
return false;
}
// check for post-var modifier
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
type = g_pElementizer->GetType();
switch (type)
{
case type_inc: // assign post-inc w/push var++
return CompileVariable_IncOrDec(0xA8, varType, varSize, varAddress, varIndexSourcePtr);
case type_dec: // assign post-dec w/push var--
return CompileVariable_IncOrDec(0xB8, varType, varSize, varAddress, varIndexSourcePtr);
case type_rnd: // assign random reverse w/push var?
return CompileVariable_Assign(0x8C, varType, varSize, varAddress, varIndexSourcePtr);
case type_til: // assign post-clear w/push var~
return CompileVariable_Assign(0x98, varType, varSize, varAddress, varIndexSourcePtr);
case type_tiltil: // assign post-set w/push var~~
return CompileVariable_Assign(0x9C, varType, varSize, varAddress, varIndexSourcePtr);
case type_assign: // assign write w/push var :=
return CompileVariable_Expression(0x80, varType, varSize, varAddress, varIndexSourcePtr);
}
unsigned char varOperator = 0x80; // assign write w/push
// var binaryop?
if (type == type_binary)
{
varOperator = 0xC0; // assign math w/swapargs w/push
varOperator |= (unsigned char)(g_pElementizer->GetOpType());
// check for '=' after binary op
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_equal)
{
return CompileVariable_Expression(varOperator, varType, varSize, varAddress, varIndexSourcePtr);
}
else
{
g_pElementizer->Backup(); // not '=' so backup
}
}
g_pElementizer->Backup(); // no post-var modifier, so backup
return CompileVariable(0, varOperator, varType, varSize, varAddress, varIndexSourcePtr);
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,404 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// CompileInstruction.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
#include "CompileUtilities.h"
// these are in CompileExpression.cpp
extern bool CompileTerm_Try(unsigned char anchor);
extern bool CompileTerm_Sub(unsigned char anchor, int value);
extern bool CompileTerm_ObjPub(unsigned char anchor, int value);
extern bool CompileTerm_CogNew(int value);
extern bool CompileTerm_Inst(int value);
bool CompileInst_NextQuit(int value)
{
int blockNestPtr = g_pCompilerData->bnest_ptr;
unsigned char byteCode = 0;
int caseDepth = 0;
// find repeat block
for (;;)
{
if (blockNestPtr == 0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_tioawarb];
return false;
}
unsigned char blockNestType = g_pCompilerData->bnest_type[blockNestPtr-1];
if (blockNestType == type_repeat)
{
byteCode = 0x04; // jmp 'quit'
break;
}
else if (blockNestType == type_repeat_count)
{
byteCode = 0x0B; // jnz 'quit'
break;
}
else if (blockNestType == type_if)
{
// ignore 'if' block nest(s)
}
else if (blockNestType == type_case) // allow nesting within 'case' block(s)
{
caseDepth += 8; // pop 2 longs for each nested 'case'
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_internal];
return false;
}
blockNestPtr--;
}
if (caseDepth > 0)
{
if (!CompileConstant(caseDepth)) // enter pop count
{
return false;
}
if (!EnterObj(0x14)) // pop
{
return false;
}
}
int blockStackPtr = g_pCompilerData->bstack_base[blockNestPtr - 1];
if ((value & 0xFF) == 0)
{
// next
if (!EnterObj(0x04)) // jmp 'next'
{
return false;
}
return CompileAddress(g_pCompilerData->bstack[blockStackPtr]);
}
// quit
if (!EnterObj(byteCode)) // jmp/jnz 'quit'
{
return false;
}
return CompileAddress(g_pCompilerData->bstack[blockStackPtr + 1]);
}
bool CompileInst_AbortReturn(int value)
{
// preview next element
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
g_pElementizer->Backup();
if (g_pElementizer->GetType() != type_end)
{
// there's an expression, compile it
if (!CompileExpression())
{
return false;
}
value |= 0x01; // +value
}
return EnterObj((unsigned char)(value & 0xFF));
}
bool CompileInst_Reboot()
{
if (!EnterObj(0x37)) // constant 0x80
{
return false;
}
if (!EnterObj(0x06))
{
return false;
}
if (!EnterObj(0x35)) // constant 0
{
return false;
}
return EnterObj(0x20); // clkset
}
bool CompileInst_CogNew(int value)
{
return CompileTerm_CogNew(value ^ 0x04); // no push
}
bool CompileInst_CogInit(int value)
{
int savedSourcePtr = g_pElementizer->GetSourcePtr();
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
int cogidSourcePtr = g_pElementizer->GetSourcePtr();
if (!SkipExpression())
{
return false;
}
if (!g_pElementizer->GetElement(type_comma))
{
return false;
}
// check for subroutine
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() != type_sub)
{
// not subroutine, so backup
g_pElementizer->SetSourcePtr(savedSourcePtr);
return CompileTerm_Inst(value); // compile assembly 'coginit'
}
// compile subroutine 'cognew' (push params+index)
int subConstant = g_pElementizer->GetValue();
if (!g_pCompilerData->bFinalCompile && g_pCompilerData->bUnusedMethodElimination)
{
AddCogNewOrInit(g_pCompilerData->current_filename, subConstant & 0x000000FF);
}
if (!CompileParameters((g_pElementizer->GetValue() & 0x0000FF00) >> 8))
{
return false;
}
if (!CompileConstant(subConstant))
{
return false;
}
if (!g_pElementizer->GetElement(type_comma))
{
return false;
}
if (!CompileExpression()) // compile stack expression
{
return false;
}
if (!g_pElementizer->GetElement(type_right))
{
return false;
}
if (!EnterObj(0x15)) // run
{
return false;
}
// compile 'cogid' exp
if (!CompileOutOfSequenceExpression(cogidSourcePtr))
{
return false;
}
if (!EnterObj(0x3F)) // regop
{
return false;
}
if (!EnterObj(0x8F)) // read+dcurr
{
return false;
}
if (!EnterObj(0x37)) // constant mask
{
return false;
}
if (!EnterObj(0x61)) // -4
{
return false;
}
if (!EnterObj(0xD1)) // write long[base][index]
{
return false;
}
return EnterObj(0x2C); // coginit
}
bool CompileInst_InstCr(int value)
{
return CompileTerm_Inst(value ^ 0x04); // no push
}
bool CompileInst_Unary(int value)
{
return CompileVariable_PreSignExtendOrRandom((unsigned char)(0x40 | (value & 0xFF)));
}
bool CompileInst_Assign(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
if (!CompileExpression())
{
return false;
}
return CompileVariable(1, vOperator, type, size, address, indexSourcePtr);
}
bool CompileInstruction()
{
int type = g_pElementizer->GetType();
int value = g_pElementizer->GetValue();
switch(type)
{
case type_back:
return CompileTerm_Try(0x03);
case type_sub:
return CompileTerm_Sub(0x01, value);
case type_obj:
return CompileTerm_ObjPub(0x01, value);
case type_i_next_quit:
return CompileInst_NextQuit(value);
case type_i_abort_return:
return CompileInst_AbortReturn(value);
case type_i_reboot:
return CompileInst_Reboot();
case type_i_cognew:
return CompileInst_CogNew(value);
case type_i_coginit:
return CompileInst_CogInit(value);
case type_i_cr: // instruction can-return
return CompileInst_InstCr(value);
case type_i_nr: // instruction never-return
return CompileTerm_Inst(value);
case type_inc: // assign pre-inc ++var
return CompileVariable_PreIncOrDec(0x20);
case type_dec: // assign pre-dec --var
return CompileVariable_PreIncOrDec(0x30);
case type_til: // assign sign-extern byte ~var
return CompileVariable_PreSignExtendOrRandom(0x10);
case type_tiltil: // assign sign-extern word ~~var
return CompileVariable_PreSignExtendOrRandom(0x14);
case type_rnd: // assign random forward ?var
return CompileVariable_PreSignExtendOrRandom(0x08);
}
g_pElementizer->SubToNeg();
if (g_pElementizer->GetType() == type_unary)
{
return CompileInst_Unary(g_pElementizer->GetOpType());
}
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
bool bVariable = false;
if (!CheckVariable(bVariable, varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
if (!bVariable)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eaiov];
return false;
}
// check for post-var modifier
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
type = g_pElementizer->GetType();
switch (type)
{
case type_inc: // assign post-inc
return CompileVariable_IncOrDec(0x28, varType, varSize, varAddress, varIndexSourcePtr);
case type_dec: // assign post-dec
return CompileVariable_IncOrDec(0x38, varType, varSize, varAddress, varIndexSourcePtr);
case type_rnd: // assign random reverse
return CompileVariable_Assign(0x0C, varType, varSize, varAddress, varIndexSourcePtr);
case type_til: // assign post-clear
return CompileVariable_Assign(0x18, varType, varSize, varAddress, varIndexSourcePtr);
case type_tiltil: // assign post-set
return CompileVariable_Assign(0x1C, varType, varSize, varAddress, varIndexSourcePtr);
case type_assign:
return CompileInst_Assign(0x1C, varType, varSize, varAddress, varIndexSourcePtr);
}
// var binaryop?
if (type == type_binary)
{
unsigned char varOperator = 0x40; // assign math w/swapargs
varOperator |= (unsigned char)(g_pElementizer->GetOpType());
// check for '=' after binary op
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_equal)
{
return CompileVariable_Expression(varOperator, varType, varSize, varAddress, varIndexSourcePtr);
}
else
{
g_pElementizer->Backup(); // not '=' so backup
}
}
g_pElementizer->Backup(); // no post-var modifier, so backup
// error, so backup and reget variable for error display
g_pElementizer->Backup();
g_pElementizer->GetNext(bEof); // this won't fail here, because it already succeeded above
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_vnao];
return false;
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,768 @@
///////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler Command Line Tool 'OpenSpin' //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// See end of file for terms of use. //
// //
///////////////////////////////////////////////////////////////
//
// CompileSpin.cpp
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CompileSpin.h"
#include "PropellerCompiler.h"
#include "objectheap.h"
#include "textconvert.h"
#include "preprocess.h"
#include "Utilities.h"
#define ObjFileStackLimit 16
#define ListLimit 2000000
#define DocLimit 2000000
static struct preprocess s_preprocessor;
CompilerData* s_pCompilerData = 0;
static int s_nObjStackPtr = 0;
static bool s_bFinalCompile;
static CompilerConfig s_compilerConfig;
static LoadFileFunc s_pLoadFileFunc = 0;
static FreeFileBufferFunc s_pFreeFileBufferFunc = 0;
static unsigned char* s_pCompileResultBuffer = 0;
static Heirarchy s_objectHeirarchy;
class ObjectNode : public HeirarchyNode
{
public:
char* m_pFullPath;
ObjectNode()
: m_pFullPath(0)
{
}
};
static bool GetPASCIISource(char* pFilename)
{
// read in file to temp buffer, convert to PASCII, and assign to s_pCompilerData->source
int nLength = 0;
char* pRawBuffer = s_pLoadFileFunc(pFilename, &nLength, &s_pCompilerData->current_file_path);
if (pRawBuffer)
{
char* pBuffer = 0;
if (s_compilerConfig.bUsePreprocessor)
{
memoryfile mfile;
mfile.buffer = pRawBuffer;
mfile.length = nLength;
mfile.readoffset = 0;
pp_push_file_struct(&s_preprocessor, &mfile, pFilename);
pp_run(&s_preprocessor);
pBuffer = pp_finish(&s_preprocessor);
nLength = (int)strlen(pBuffer);
if (nLength == 0)
{
free(pBuffer);
pBuffer = 0;
}
s_pFreeFileBufferFunc(pRawBuffer);
}
else
{
pBuffer = pRawBuffer;
}
char* pPASCIIBuffer = new char[nLength+1];
memset(pPASCIIBuffer, 0, nLength + 1);
if (!UnicodeToPASCII(pBuffer, nLength, pPASCIIBuffer, s_compilerConfig.bUsePreprocessor))
{
printf("Unrecognized text encoding format!\n");
delete [] pPASCIIBuffer;
if (s_compilerConfig.bUsePreprocessor)
{
free(pBuffer);
}
else
{
s_pFreeFileBufferFunc(pRawBuffer);
}
return false;
}
// clean up any previous buffer
if (s_pCompilerData->source)
{
delete [] s_pCompilerData->source;
}
s_pCompilerData->source = pPASCIIBuffer;
if (s_compilerConfig.bUsePreprocessor)
{
free(pBuffer);
}
else
{
s_pFreeFileBufferFunc(pRawBuffer);
}
}
else
{
s_pCompilerData->source = NULL;
return false;
}
return true;
}
static void CleanupMemory(bool bUnusedMethodData = true)
{
delete s_objectHeirarchy.m_pRoot;
s_objectHeirarchy.m_pRoot = 0;
if ( s_pCompilerData )
{
delete [] s_pCompilerData->list;
delete [] s_pCompilerData->doc;
delete [] s_pCompilerData->obj;
delete [] s_pCompilerData->source;
}
CleanObjectHeap();
if (bUnusedMethodData)
{
CleanUpUnusedMethodData();
}
Cleanup();
if (s_pCompileResultBuffer != 0)
{
delete [] s_pCompileResultBuffer;
s_pCompileResultBuffer = 0;
}
}
void PrintError(const char* pFilename, const char* pErrorString)
{
int lineNumber = 1;
int column = 1;
int offsetToStartOfLine = -1;
int offsetToEndOfLine = -1;
int offendingItemStart = 0;
int offendingItemEnd = 0;
GetErrorInfo(lineNumber, column, offsetToStartOfLine, offsetToEndOfLine, offendingItemStart, offendingItemEnd);
printf("%s(%d:%d) : error : %s\n", pFilename, lineNumber, column, pErrorString);
if ( offendingItemStart == offendingItemEnd && s_pCompilerData->source[offendingItemStart] == 0 )
{
printf("Line:\nEnd Of File\nOffending Item: N/A\n");
}
else
{
char* errorLine = 0;
char* errorItem = 0;
if (offendingItemEnd - offendingItemStart > 0)
{
errorLine = new char[(offsetToEndOfLine - offsetToStartOfLine) + 1];
strncpy(errorLine, &s_pCompilerData->source[offsetToStartOfLine], offsetToEndOfLine - offsetToStartOfLine);
errorLine[offsetToEndOfLine - offsetToStartOfLine] = 0;
}
if (offendingItemEnd - offendingItemStart > 0)
{
errorItem = new char[(offendingItemEnd - offendingItemStart) + 1];
strncpy(errorItem, &s_pCompilerData->source[offendingItemStart], offendingItemEnd - offendingItemStart);
errorItem[offendingItemEnd - offendingItemStart] = 0;
}
printf("Line:\n%s\nOffending Item: %s\n", errorLine ? errorLine : "N/A", errorItem ? errorItem : "N/A");
delete [] errorLine;
delete [] errorItem;
}
}
static bool CheckForCircularReference(ObjectNode* pObjectNode)
{
ObjectNode* pCurrentNode = pObjectNode;
while (pCurrentNode->m_pParent)
{
ObjectNode* pParent = (ObjectNode*)(pCurrentNode->m_pParent);
if (strcmp(pObjectNode->m_pFullPath, pParent->m_pFullPath) == 0)
{
return true;
}
pCurrentNode = pParent;
}
return false;
}
static bool CompileRecursively(char* pFilename, int& nCompileIndex, ObjectNode* pParentNode)
{
nCompileIndex++;
if (s_nObjStackPtr > 0 && (!s_compilerConfig.bQuiet || s_compilerConfig.bFileTreeOutputOnly))
{
// only do this if UME is off or if it's the final compile when UME is on
if (!s_compilerConfig.bUnusedMethodElimination || s_pCompilerData->bFinalCompile)
{
char spaces[] = " \0";
printf("%s|-%s\n", &spaces[32-(s_nObjStackPtr<<1)], pFilename);
}
}
s_nObjStackPtr++;
if (s_nObjStackPtr > ObjFileStackLimit)
{
printf("%s : error : Object nesting exceeds limit of %d levels.\n", pFilename, ObjFileStackLimit);
return false;
}
void *definestate = 0;
if (s_compilerConfig.bUsePreprocessor)
{
definestate = pp_get_define_state(&s_preprocessor);
}
if (!GetPASCIISource(pFilename))
{
printf("%s : error : Can not find/open file.\n", pFilename);
return false;
}
if (!s_pCompilerData->bFinalCompile && s_compilerConfig.bUnusedMethodElimination)
{
AddObjectName(pFilename, nCompileIndex);
}
strcpy(s_pCompilerData->current_filename, pFilename);
char* pExtension = strstr(s_pCompilerData->current_filename, ".spin");
if (pExtension != 0)
{
*pExtension = 0;
}
ObjectNode* pObjectNode = new ObjectNode();
pObjectNode->m_pFullPath = s_pCompilerData->current_file_path;
pObjectNode->m_pParent = pParentNode;
s_objectHeirarchy.AddNode(pObjectNode, pParentNode);
if (CheckForCircularReference(pObjectNode))
{
printf("%s : error : Illegal Circular Reference\n", pFilename);
return false;
}
// first pass on object
const char* pErrorString = Compile1();
if (pErrorString != 0)
{
PrintError(pFilename, pErrorString);
return false;
}
if (s_pCompilerData->obj_files > 0)
{
char filenames[file_limit*256];
int numObjects = s_pCompilerData->obj_files;
for (int i = 0; i < numObjects; i++)
{
// copy the obj filename appending .spin if it doesn't have it.
strcpy(&filenames[i<<8], &(s_pCompilerData->obj_filenames[i<<8]));
if (strstr(&filenames[i<<8], ".spin") == NULL)
{
strcat(&filenames[i<<8], ".spin");
}
}
for (int i = 0; i < numObjects; i++)
{
if (!CompileRecursively(&filenames[i<<8], nCompileIndex, pObjectNode))
{
return false;
}
}
// redo first pass on parent object
if (s_compilerConfig.bUsePreprocessor)
{
// undo any defines in sub-objects
pp_restore_define_state(&s_preprocessor, definestate);
}
if (!GetPASCIISource(pFilename))
{
printf("%s : error : Can not find/open file.\n", pFilename);
return false;
}
strcpy(s_pCompilerData->current_filename, pFilename);
pExtension = strstr(s_pCompilerData->current_filename, ".spin");
if (pExtension != 0)
{
*pExtension = 0;
}
pErrorString = Compile1();
if (pErrorString != 0)
{
PrintError(pFilename, pErrorString);
return false;
}
if (!CopyObjectsFromHeap(s_pCompilerData, filenames))
{
printf("%s : error : Object files exceed 128k.\n", pFilename);
return false;
}
}
// load all DAT files
if (s_pCompilerData->dat_files > 0)
{
int p = 0;
for (int i = 0; i < s_pCompilerData->dat_files; i++)
{
// Get DAT's Files
// Get name information
char filename[256];
strcpy(&filename[0], &(s_pCompilerData->dat_filenames[i<<8]));
// Load file and add to dat_data buffer
s_pCompilerData->dat_lengths[i] = -1;
char* pFilePath = 0;
char* pBuffer = s_pLoadFileFunc(&filename[0], &s_pCompilerData->dat_lengths[i], &pFilePath);
if (s_pCompilerData->dat_lengths[i] == -1)
{
s_pCompilerData->dat_lengths[i] = 0;
printf("Cannot find/open dat file: %s \n", &filename[0]);
return false;
}
if (p + s_pCompilerData->dat_lengths[i] > data_limit)
{
printf("%s : error : DAT files exceed 128k.\n", pFilename);
return false;
}
memcpy(&(s_pCompilerData->dat_data[p]), pBuffer, s_pCompilerData->dat_lengths[i]);
s_pFreeFileBufferFunc(pBuffer);
s_pCompilerData->dat_offsets[i] = p;
p += s_pCompilerData->dat_lengths[i];
}
}
// second pass of object
pErrorString = Compile2();
if (pErrorString != 0)
{
PrintError(pFilename, pErrorString);
return false;
}
// only do this check if UME is off or if it's the final compile when UME is on
if (!s_compilerConfig.bUnusedMethodElimination || s_pCompilerData->bFinalCompile)
{
// Check to make sure object fits into 32k (or eeprom size if specified as larger than 32k)
unsigned int i = 0x10 + s_pCompilerData->psize + s_pCompilerData->vsize + (s_pCompilerData->stack_requirement << 2);
if ((s_pCompilerData->compile_mode == 0) && (i > s_pCompilerData->eeprom_size))
{
printf("%s : error : Object exceeds runtime memory limit by %d longs.\n", pFilename, (i - s_pCompilerData->eeprom_size) >> 2);
return false;
}
}
// save this object in the heap
if (!AddObjectToHeap(pFilename, s_pCompilerData))
{
printf("%s : error : Object Heap Overflow.\n", pFilename);
return false;
}
s_nObjStackPtr--;
return true;
}
static bool ComposeRAM(unsigned char** ppBuffer, int& bufferSize)
{
if (!s_compilerConfig.bDATonly)
{
unsigned int varsize = s_pCompilerData->vsize; // variable size (in bytes)
unsigned int codsize = s_pCompilerData->psize; // code size (in bytes)
unsigned int pubaddr = *((unsigned short*)&(s_pCompilerData->obj[8])); // address of first public method
unsigned int publocs = *((unsigned short*)&(s_pCompilerData->obj[10])); // number of stack variables (locals), in bytes, for the first public method
unsigned int pbase = 0x0010; // base of object code
unsigned int vbase = pbase + codsize; // variable base = object base + code size
unsigned int dbase = vbase + varsize + 8; // data base = variable base + variable size + 8
unsigned int pcurr = pbase + pubaddr; // Current program start = object base + public address (first public method)
unsigned int dcurr = dbase + 4 + (s_pCompilerData->first_pub_parameters << 2) + publocs; // current data stack pointer = data base + 4 + FirstParams*4 + publocs
if (s_compilerConfig.bBinary)
{
// reset ram
*ppBuffer = new unsigned char[vbase];
memset(*ppBuffer, 0, vbase);
bufferSize = vbase;
}
else
{
if (vbase + 8 > s_compilerConfig.eeprom_size)
{
printf("ERROR: eeprom size exceeded by %d longs.\n", (vbase + 8 - s_compilerConfig.eeprom_size) >> 2);
return false;
}
// reset ram
*ppBuffer = new unsigned char[s_compilerConfig.eeprom_size];
memset(*ppBuffer, 0, s_compilerConfig.eeprom_size);
bufferSize = s_compilerConfig.eeprom_size;
(*ppBuffer)[dbase-8] = 0xFF;
(*ppBuffer)[dbase-7] = 0xFF;
(*ppBuffer)[dbase-6] = 0xF9;
(*ppBuffer)[dbase-5] = 0xFF;
(*ppBuffer)[dbase-4] = 0xFF;
(*ppBuffer)[dbase-3] = 0xFF;
(*ppBuffer)[dbase-2] = 0xF9;
(*ppBuffer)[dbase-1] = 0xFF;
}
// set clock frequency and clock mode
*((int*)&((*ppBuffer)[0])) = s_pCompilerData->clkfreq;
(*ppBuffer)[4] = s_pCompilerData->clkmode;
// set interpreter parameters
((unsigned short*)&((*ppBuffer)[4]))[1] = (unsigned short)pbase; // always 0x0010
((unsigned short*)&((*ppBuffer)[4]))[2] = (unsigned short)vbase;
((unsigned short*)&((*ppBuffer)[4]))[3] = (unsigned short)dbase;
((unsigned short*)&((*ppBuffer)[4]))[4] = (unsigned short)pcurr;
((unsigned short*)&((*ppBuffer)[4]))[5] = (unsigned short)dcurr;
// set code
memcpy(&((*ppBuffer)[pbase]), &(s_pCompilerData->obj[4]), codsize);
// install ram checksum byte
unsigned char sum = 0;
for (unsigned int i = 0; i < vbase; i++)
{
sum = sum + (*ppBuffer)[i];
}
(*ppBuffer)[5] = (unsigned char)((-(sum+2028)) );
}
else
{
unsigned int objsize = *((unsigned short*)&(s_pCompilerData->obj[4]));
if (s_pCompilerData->psize > 65535)
{
objsize = s_pCompilerData->psize;
}
unsigned int size = objsize - 4 - (s_pCompilerData->obj[7] * 4);
*ppBuffer = new unsigned char[size];
bufferSize = size;
memcpy(&((*ppBuffer)[0]), &(s_pCompilerData->obj[8 + (s_pCompilerData->obj[7] * 4)]), size);
}
return true;
}
static void DumpSymbols()
{
for (int i = 0; i < s_pCompilerData->info_count; i++)
{
char szTemp[256];
szTemp[0] = '*';
szTemp[1] = 0;
int length = 0;
int start = 0;
if (s_pCompilerData->info_type[i] == info_pub || s_pCompilerData->info_type[i] == info_pri)
{
length = s_pCompilerData->info_data3[i] - s_pCompilerData->info_data2[i];
start = s_pCompilerData->info_data2[i];
}
else if (s_pCompilerData->info_type[i] != info_dat && s_pCompilerData->info_type[i] != info_dat_symbol)
{
length = s_pCompilerData->info_finish[i] - s_pCompilerData->info_start[i];
start = s_pCompilerData->info_start[i];
}
if (length > 0 && length < 256)
{
strncpy(szTemp, &s_pCompilerData->source[start], length);
szTemp[length] = 0;
}
switch(s_pCompilerData->info_type[i])
{
case info_con:
printf("CON, %s, %d\n", szTemp, s_pCompilerData->info_data0[i]);
break;
case info_con_float:
printf("CONF, %s, %f\n", szTemp, *((float*)&(s_pCompilerData->info_data0[i])));
break;
case info_pub_param:
{
char szTemp2[256];
szTemp2[0] = '*';
szTemp2[1] = 0;
length = s_pCompilerData->info_data3[i] - s_pCompilerData->info_data2[i];
start = s_pCompilerData->info_data2[i];
if (length > 0 && length < 256)
{
strncpy(szTemp2, &s_pCompilerData->source[start], length);
szTemp2[length] = 0;
}
printf("PARAM, %s, %s, %d, %d\n", szTemp2, szTemp, s_pCompilerData->info_data0[i], s_pCompilerData->info_data1[i]);
}
break;
case info_pub:
printf("PUB, %s, %d, %d\n", szTemp, s_pCompilerData->info_data4[i] & 0xFFFF, s_pCompilerData->info_data4[i] >> 16);
break;
}
}
}
static void DumpList()
{
size_t listOffset = 0;
while (listOffset < s_pCompilerData->list_length)
{
char* pTemp = strstr(&(s_pCompilerData->list[listOffset]), "\r");
if (pTemp)
{
*pTemp = 0;
}
printf("%s\n", &(s_pCompilerData->list[listOffset]));
if (pTemp)
{
*pTemp = 0x0D;
listOffset += (pTemp - &(s_pCompilerData->list[listOffset])) + 1;
}
else
{
listOffset += strlen(&(s_pCompilerData->list[listOffset]));
}
}
}
static void DumpDoc()
{
size_t docOffset = 0;
while (docOffset < s_pCompilerData->doc_length)
{
char* pTemp = strstr(&(s_pCompilerData->doc[docOffset]), "\r");
if (pTemp)
{
*pTemp = 0;
}
printf("%s\n", &(s_pCompilerData->doc[docOffset]));
if (pTemp)
{
*pTemp = 0x0D;
docOffset += (pTemp - &(s_pCompilerData->doc[docOffset])) + 1;
}
else
{
docOffset += strlen(&(s_pCompilerData->doc[docOffset]));
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void InitCompiler(CompilerConfig* pCompilerConfig, LoadFileFunc pLoadFileFunc, FreeFileBufferFunc pFreeFileBufferFunc)
{
s_nObjStackPtr = 0;
s_pCompilerData = 0;
s_bFinalCompile = false;
s_pCompileResultBuffer = 0;
if (pCompilerConfig)
{
s_compilerConfig = *pCompilerConfig;
}
s_pLoadFileFunc = pLoadFileFunc;
s_pFreeFileBufferFunc = pFreeFileBufferFunc;
pp_setFileFunctions(pLoadFileFunc, pFreeFileBufferFunc);
pp_init(&s_preprocessor, s_compilerConfig.bAlternatePreprocessorMode);
pp_setcomments(&s_preprocessor, "\'", "{", "}");
}
void SetDefine(const char* pName, const char* pValue)
{
pp_define(&s_preprocessor, pName, pValue);
}
unsigned char* CompileSpin(char* pFilename, int* pnResultLength)
{
*pnResultLength = 0;
if (s_compilerConfig.bFileTreeOutputOnly)
{
printf("%s\n", pFilename);
}
if (s_compilerConfig.bUnusedMethodElimination)
{
InitUnusedMethodData();
}
int nOriginalSize = 0;
restart_compile:
s_pCompilerData = InitStruct();
s_pCompilerData->bUnusedMethodElimination = s_compilerConfig.bUnusedMethodElimination;
s_pCompilerData->bFinalCompile = s_bFinalCompile;
s_pCompilerData->list = new char[ListLimit];
s_pCompilerData->list_limit = ListLimit;
memset(s_pCompilerData->list, 0, ListLimit);
if (s_compilerConfig.bDocMode && !s_compilerConfig.bDATonly)
{
s_pCompilerData->doc = new char[DocLimit];
s_pCompilerData->doc_limit = DocLimit;
memset(s_pCompilerData->doc, 0, DocLimit);
}
else
{
s_pCompilerData->doc = 0;
s_pCompilerData->doc_limit = 0;
}
s_pCompilerData->bDATonly = s_compilerConfig.bDATonly;
s_pCompilerData->bBinary = s_compilerConfig.bBinary;
s_pCompilerData->eeprom_size = s_compilerConfig.eeprom_size;
// allocate space for obj based on eeprom size command line option
s_pCompilerData->obj_limit = s_compilerConfig.eeprom_size > min_obj_limit ? s_compilerConfig.eeprom_size : min_obj_limit;
s_pCompilerData->obj = new unsigned char[s_pCompilerData->obj_limit];
// copy filename into obj_title, and chop off the .spin
strcpy(s_pCompilerData->obj_title, pFilename);
char* pExtension = strstr(&s_pCompilerData->obj_title[0], ".spin");
if (pExtension != 0)
{
*pExtension = 0;
}
int nCompileIndex = 0;
if (!CompileRecursively(pFilename, nCompileIndex, 0))
{
return 0;
}
if (!s_compilerConfig.bQuiet)
{
// only do this if UME is off or if it's the final compile when UME is on
if (!s_compilerConfig.bUnusedMethodElimination || s_bFinalCompile)
{
printf("Done.\n");
}
}
if (!s_compilerConfig.bFileTreeOutputOnly && !s_compilerConfig.bFileListOutputOnly && !s_compilerConfig.bDumpSymbols)
{
if (!s_bFinalCompile && s_compilerConfig.bUnusedMethodElimination)
{
nOriginalSize = s_pCompilerData->psize;
FindUnusedMethods(s_pCompilerData);
s_bFinalCompile = true;
CleanupMemory(false);
goto restart_compile;
}
int bufferSize = 0;
if (!ComposeRAM(&s_pCompileResultBuffer, bufferSize))
{
return 0;
}
if (!s_compilerConfig.bQuiet)
{
if (s_compilerConfig.bUnusedMethodElimination)
{
printf("Unused Method Elimination:\n");
if ((nOriginalSize - s_pCompilerData->psize) > 0)
{
if (s_compilerConfig.bVerbose)
{
if (s_pCompilerData->unused_obj_files)
{
printf("Unused Objects:\n");
for(int i = 0; i < s_pCompilerData->unused_obj_files; i++)
{
printf("%s\n", &(s_pCompilerData->obj_unused[i<<8]));
}
}
if (s_pCompilerData->unused_methods)
{
printf("Unused Methods:\n");
for(int i = 0; i < s_pCompilerData->unused_methods; i++)
{
printf("%s\n", &(s_pCompilerData->method_unused[i*symbol_limit]));
}
}
if (s_pCompilerData->unused_methods || s_pCompilerData->unused_obj_files)
{
printf("---------------\n");
}
}
printf("%5d methods removed\n%5d objects removed\n%5d bytes saved\n", s_pCompilerData->unused_methods, s_pCompilerData->unused_obj_files, nOriginalSize - s_pCompilerData->psize );
}
else
{
printf("Nothing removed.\n");
}
printf("--------------------------\n");
}
printf("Program size is %d bytes\n", bufferSize);
}
*pnResultLength = bufferSize;
}
if (s_compilerConfig.bDumpSymbols)
{
DumpSymbols();
}
if (s_compilerConfig.bVerbose && !s_compilerConfig.bQuiet && !s_compilerConfig.bDATonly)
{
DumpList();
}
if (s_compilerConfig.bDocMode && s_compilerConfig.bVerbose && !s_compilerConfig.bQuiet && !s_compilerConfig.bDATonly)
{
DumpDoc();
}
return s_pCompileResultBuffer;
}
void ShutdownCompiler()
{
pp_clear_define_state(&s_preprocessor);
CleanupMemory();
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,76 @@
///////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler Command Line Tool 'OpenSpin' //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// See end of file for terms of use. //
// //
///////////////////////////////////////////////////////////////
//
// CompileSpin.h
//
#ifndef _COMPILESPIN_H_
#define _COMPILESPIN_H_
typedef char* (*LoadFileFunc)(const char* pFilename, int* pnLength, char** ppFilePath);
typedef void (*FreeFileBufferFunc)(char* pBuffer);
struct CompilerConfig
{
CompilerConfig()
: bVerbose(false)
, bQuiet(false)
, bFileTreeOutputOnly(false)
, bFileListOutputOnly(false)
, bDumpSymbols(false)
, bUsePreprocessor(true)
, bAlternatePreprocessorMode(false)
, bUnusedMethodElimination(false)
, bDocMode(false)
, bDATonly(false)
, bBinary(true)
, eeprom_size(32768)
{
}
bool bVerbose;
bool bQuiet;
bool bFileTreeOutputOnly;
bool bFileListOutputOnly;
bool bDumpSymbols;
bool bUsePreprocessor;
bool bAlternatePreprocessorMode;
bool bUnusedMethodElimination;
bool bDocMode;
bool bDATonly;
bool bBinary;
unsigned int eeprom_size;
};
void InitCompiler(CompilerConfig* pCompilerConfig, LoadFileFunc pLoadFileFunc, FreeFileBufferFunc pFreeFileBufferFunc);
void SetDefine(const char* pName, const char* pValue);
unsigned char* CompileSpin(char* pFilename, int* pnResultLength);
void ShutdownCompiler();
#endif // _COMPILESPIN_H_
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,686 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// CompileUtilities.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
#include "CompileUtilities.h"
bool SkipBlock(int column)
{
int savedObjPtr = g_pCompilerData->obj_ptr;
bool savedStringPatchEnable = g_pCompilerData->str_patch_enable;
g_pCompilerData->str_patch_enable = false;
if (!CompileBlock(column))
{
return false;
}
g_pCompilerData->str_patch_enable = savedStringPatchEnable;
g_pCompilerData->obj_ptr = savedObjPtr;
return true;
}
bool SkipRange()
{
int savedObjPtr = g_pCompilerData->obj_ptr;
bool savedStringPatchEnable = g_pCompilerData->str_patch_enable;
g_pCompilerData->str_patch_enable = false;
bool bRange;
if (!CompileRange(bRange))
{
return false;
}
g_pCompilerData->str_patch_enable = savedStringPatchEnable;
g_pCompilerData->obj_ptr = savedObjPtr;
return true;
}
bool SkipExpression()
{
int savedObjPtr = g_pCompilerData->obj_ptr;
bool savedStringPatchEnable = g_pCompilerData->str_patch_enable;
g_pCompilerData->str_patch_enable = false;
if (!CompileExpression())
{
return false;
}
g_pCompilerData->str_patch_enable = savedStringPatchEnable;
g_pCompilerData->obj_ptr = savedObjPtr;
return true;
}
bool CheckIndex(bool& bIndex, int& expSourcePtr)
{
bIndex = false;
if (g_pElementizer->CheckElement(type_leftb))
{
expSourcePtr = g_pElementizer->GetSourcePtr();
if (!SkipExpression())
{
return false;
}
if (!g_pElementizer->GetElement(type_rightb))
{
return false;
}
bIndex = true;
}
return true;
}
bool CheckIndexRange(bool& bIndex, int& expSourcePtr)
{
bIndex = false;
if (g_pElementizer->CheckElement(type_leftb))
{
expSourcePtr = g_pElementizer->GetSourcePtr();
if (!SkipExpression())
{
return false;
}
if (g_pElementizer->CheckElement(type_dotdot))
{
if (!SkipExpression())
{
return false;
}
}
if (!g_pElementizer->GetElement(type_rightb))
{
return false;
}
bIndex = true;
}
return true;
}
bool CheckVariable_AddressExpression(int& expSourcePtr)
{
bool bIndex = false;
if (!CheckIndex(bIndex, expSourcePtr))
{
return false;
}
if (!bIndex)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eleftb];
return false;
}
return true;
}
bool CheckVariable(bool& bVariable, unsigned char& type, unsigned char& size, int& address, int& indexSourcePtr)
{
address = g_pElementizer->GetValue();
indexSourcePtr = 0;
unsigned char varType = (unsigned char)(g_pElementizer->GetType() & 0xFF);
if (varType >= type_var_byte && varType <= type_var_long)
{
type = type_var_byte;
// adjust address base on the var size
if (varType < type_var_long)
{
address += g_pCompilerData->var_long;
}
if (varType == type_var_byte)
{
address += g_pCompilerData->var_word;
}
}
else if (varType >= type_dat_byte && varType <= type_dat_long)
{
type = type_dat_byte;
}
else if (varType >= type_loc_byte && varType <= type_loc_long)
{
type = type_loc_byte;
}
else
{
type = varType;
if (varType == type_size)
{
size = (unsigned char)(g_pElementizer->GetValue() & 0xFF);
if (!CheckVariable_AddressExpression(address))
{
return false;
}
bool bIndex = false;
if (!CheckIndex(bIndex, indexSourcePtr))
{
return false;
}
bVariable = true;
return true;
}
else
{
size = 2;
if (varType == type_spr)
{
if (!CheckVariable_AddressExpression(address))
{
return false;
}
bVariable = true;
return true;
}
else if (varType == type_reg)
{
bool bIndex = false;
if (!CheckIndexRange(bIndex, indexSourcePtr))
{
return false;
}
if (bIndex)
{
size = 3;
}
bVariable = true;
return true;
}
else
{
bVariable = false;
return true;
}
}
}
// if we got here then it's a var/dat/loc type
// set size
size = varType;
size -= type;
bool bIndex = false;
if (!CheckIndex(bIndex, indexSourcePtr))
{
return false;
}
if (!bIndex)
{
// check for .byte/word/long{[index]}
if (g_pElementizer->CheckElement(type_dot))
{
bool bEof = false;
if (!g_pElementizer->GetNext(bEof)) // get byte/word/long
{
return false;
}
if (g_pElementizer->GetType() != type_size)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_ebwol];
return false;
}
if (size < (g_pElementizer->GetValue() & 0xFF)) // new size must be same or smaller
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_sombs];
return false;
}
size = (g_pElementizer->GetValue() & 0xFF); // update size
bool bIndexCheck = false;
if (!CheckIndex(bIndexCheck, indexSourcePtr))
{
return false;
}
}
}
bVariable = true;
return true;
}
bool GetVariable(unsigned char& type, unsigned char& size, int& address, int& indexSourcePtr)
{
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
bool bVariable = false;
if (!CheckVariable(bVariable, type, size, address, indexSourcePtr))
{
return false;
}
if (!bVariable)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eav];
return false;
}
return true;
}
bool CompileVariable(unsigned char vOperation, unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
// compile and index(s)
if (type != type_reg)
{
if (type == type_spr || type == type_size)
{
if (!CompileOutOfSequenceExpression(address))
{
return false;
}
}
if (type != type_spr)
{
if (indexSourcePtr != 0)
{
if (!CompileOutOfSequenceExpression(indexSourcePtr))
{
return false;
}
}
}
}
unsigned char byteCode = 0;
if (type == type_spr)
{
byteCode = 0x24 | vOperation;
}
else if (type == type_reg)
{
byteCode = 0x3F;
if (size != 2)
{
bool bRange = false;
if (!CompileOutOfSequenceRange(indexSourcePtr, bRange))
{
return false;
}
if (bRange)
{
byteCode = 0x3E;
}
else
{
byteCode = 0x3D;
}
}
if (!EnterObj(byteCode))
{
return false;
}
// byteCode = 1 in high bit, bottom 2 bits of vOperation in next two bits, then bottom 5 bits of address
byteCode = 0x80 | ((vOperation & 3) << 5) | (address & 0x1F);
}
else
{
if ((type != type_var_byte && type != type_loc_byte) || size != 2 || address >= 8*4 || indexSourcePtr != 0)
{
// not compact
byteCode = 0x80 | (size << 5);
if (indexSourcePtr != 0)
{
byteCode |= 0x10;
}
byteCode |= vOperation;
if (type != type_size)
{
if (type == type_dat_byte)
{
byteCode += 4;
}
else if (type == type_var_byte)
{
byteCode += 8;
}
else if (type == type_loc_byte)
{
byteCode += 12;
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_internal];
return false;
}
if (!EnterObj(byteCode))
{
return false;
}
if (address > 0x7F)
{
// two byte address
byteCode = (unsigned char)(address >> 8) | 0x80;
if (!EnterObj(byteCode))
{
return false;
}
}
byteCode = (unsigned char)address;
}
}
else
{
// compact
byteCode = (type == type_var_byte) ? 0x40 : 0x60;
byteCode |= (unsigned char)address;
byteCode |= vOperation;
}
}
if (!EnterObj(byteCode))
{
return false;
}
if (vOperation == 2) // if assign
{
if (!EnterObj(vOperator))
{
return false;
}
}
return true;
}
bool CompileVariable_Assign(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
return CompileVariable(2, vOperator, type, size, address, indexSourcePtr);
}
bool CompileVariable_Expression(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
if (!CompileExpression())
{
return false;
}
return CompileVariable(2, vOperator, type, size, address, indexSourcePtr);
}
bool CompileVariable_PreSignExtendOrRandom(unsigned char vOperator)
{
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
if (!GetVariable(varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
return CompileVariable_Assign(vOperator, varType, varSize, varAddress, varIndexSourcePtr);
}
bool CompileVariable_IncOrDec(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr)
{
return CompileVariable(2, vOperator | (((size + 1) & 3) << 1), type, size, address, indexSourcePtr);
}
bool CompileVariable_PreIncOrDec(unsigned char vOperator)
{
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
if (!GetVariable(varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
return CompileVariable_IncOrDec(vOperator, varType, varSize, varAddress, varIndexSourcePtr);
}
bool CompileParameters(int numParameters)
{
if (numParameters > 0)
{
if (!g_pElementizer->GetElement(type_left)) // (
{
return false;
}
for (int i = 0; i < numParameters; i++)
{
if (!CompileExpression())
{
return false;
}
if (i < (numParameters - 1))
{
if (!g_pElementizer->GetElement(type_comma))
{
return false;
}
}
}
if (!g_pElementizer->GetElement(type_right)) // )
{
return false;
}
}
return true;
}
bool CompileConstant(int value)
{
if (value >= -1 && value <= 1)
{
// constant is -1, 0, or 1, so compiles to a single bytecode
unsigned char byteCode = (unsigned char)(value+1) | 0x34;
if (!EnterObj(byteCode))
{
return false;
}
return true;
}
// see if it's a mask
// masks can be: only one bit on (e.g. 0x00008000),
// all bits on except one (e.g. 0xFFFF7FFF),
// all bits on up to a bit then all zeros (e.g. 0x0000FFFF),
// or all bits off up to a bit then all ones (e.g. 0xFFFF0000)
for (unsigned char i = 0; i < 128; i++)
{
int testVal = 2;
testVal <<= (i & 0x1F); // mask i, so that we only actually shift 0 to 31
if (i & 0x20) // i in range 32 to 63 or 96 to 127
{
testVal--;
}
if (i& 0x40) // i in range 64 to 127
{
testVal = ~testVal;
}
if (testVal == value)
{
if (!EnterObj(0x37)) // (constant mask)
{
return false;
}
if (!EnterObj(i))
{
return false;
}
return true;
}
}
// handle constants with upper 2 or 3 bytes being 0xFFs, using 'not'
if ((value & 0xFFFFFF00) == 0xFFFFFF00)
{
// one byte constant using 'not'
if (!EnterObj(0x38))
{
return false;
}
unsigned char byteCode = (unsigned char)(value & 0xFF);
if (!EnterObj(~byteCode))
{
return false;
}
if (!EnterObj(0xE7)) // (bitwise bot)
{
return false;
}
return true;
}
else if ((value & 0xFFFF0000) == 0xFFFF0000)
{
// two byte constant using 'not'
if (!EnterObj(0x39))
{
return false;
}
unsigned char byteCode = (unsigned char)((value >> 8) & 0xFF);
if (!EnterObj(~byteCode))
{
return false;
}
byteCode = (unsigned char)(value & 0xFF);
if (!EnterObj(~byteCode))
{
return false;
}
if (!EnterObj(0xE7)) // (bitwise bot)
{
return false;
}
return true;
}
// 1 to 4 byte constant
unsigned char size = 1;
if (value & 0xFF000000)
{
size = 4;
}
else if (value & 0x00FF0000)
{
size = 3;
}
else if (value & 0x0000FF00)
{
size = 2;
}
unsigned char byteCode = 0x37 + size; // (constant 1..4 bytes)
if (!EnterObj(byteCode))
{
return false;
}
for (unsigned char i = size; i > 0; i--)
{
byteCode = (unsigned char)((value >> ((i - 1) * 8)) & 0xFF);
if (!EnterObj(byteCode))
{
return false;
}
}
return true;
}
bool CompileOutOfSequenceExpression(int sourcePtr)
{
int savedSourcePtr = g_pElementizer->GetSourcePtr();
g_pElementizer->SetSourcePtr(sourcePtr);
if (!CompileExpression())
{
return false;
}
g_pElementizer->SetSourcePtr(savedSourcePtr);
return true;
}
bool CompileOutOfSequenceRange(int sourcePtr, bool& bRange)
{
int savedSourcePtr = g_pElementizer->GetSourcePtr();
g_pElementizer->SetSourcePtr(sourcePtr);
if (!CompileRange(bRange))
{
return false;
}
g_pElementizer->SetSourcePtr(savedSourcePtr);
return true;
}
// compiles either a value or a range and sets the bRange flag accordingly
bool CompileRange(bool& bRange)
{
if (!CompileExpression())
{
return false;
}
if (g_pElementizer->CheckElement(type_dotdot))
{
if (!CompileExpression())
{
return false;
}
bRange = true;
}
else
{
bRange = false;
}
return true;
}
// Compile relative address
bool CompileAddress(int address)
{
address -= g_pCompilerData->obj_ptr; // make relative address
address--; // compensate for single-byte
if ((address < 0 && abs(address) <= 64) || (address >= 0 && address < 64))
{
// single byte, enter
address &= 0x007F;
}
else
{
// double byte, compensate and enter
address--;
if (!EnterObj((unsigned char)((address >> 8) | 0x80)))
{
return false;
}
address &= 0x00FF;
}
return EnterObj((unsigned char)address);
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,80 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// CompileUtilities.h
//
#ifndef _COMPILEUTILITIES_H_
#define _COMPILEUTILITIES_H_
extern bool SkipBlock(int column);
extern bool SkipRange();
extern bool SkipExpression();
extern bool CheckIndex(bool& bIndex, int& expSourcePtr);
extern bool CheckIndexRange(bool& bIndex, int& expSourcePtr);
extern bool CheckVariable(bool& bVariable, unsigned char& type, unsigned char& size, int& address, int& indexSourcePtr);
extern bool GetVariable(unsigned char& type, unsigned char& size, int& address, int& indexSourcePtr);
extern bool CompileVariable(unsigned char vOperation, unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr);
extern bool CompileVariable_Assign(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr);
extern bool CompileVariable_Expression(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr);
extern bool CompileVariable_PreSignExtendOrRandom(unsigned char vOperator);
extern bool CompileVariable_IncOrDec(unsigned char vOperator, unsigned char type, unsigned char size, int address, int indexSourcePtr);
extern bool CompileVariable_PreIncOrDec(unsigned char vOperator);
extern bool CompileParameters(int numParameters);
extern bool CompileConstant(int value);
extern bool CompileOutOfSequenceExpression(int sourcePtr);
extern bool CompileOutOfSequenceRange(int sourcePtr, bool& bRange);
extern bool CompileRange(bool& bRange);
extern bool CompileAddress(int address);
// these are in InstructionBlockCompiler.cpp
extern bool CompileBlock(int column);
extern bool OptimizeBlock(int column, int param, bool (*pCompileFunction)(int, int));
extern bool CompileInstruction(); // in CompileInstruction.cpp
extern bool CompileExpression(); // in CompileExpression.cpp
// these are in StringConstantRoutines.cpp
extern void StringConstant_PreProcess();
extern bool StringConstant_GetIndex();
extern bool StringConstant_EnterChar(unsigned char theChar);
extern void StringConstant_EnterPatch();
extern bool StringConstant_PostProcess();
// these are int BlockNestStackRoutines.cpp
extern bool BlockNest_New(unsigned char type, int stackSize);
extern void BlockNest_Redo(unsigned char type);
extern void BlockNest_End();
extern void BlockStack_Write(int address, int value);
extern int BlockStack_Read(int address);
extern bool BlockStack_CompileAddress(int address);
extern bool BlockStack_CompileConstant();
#endif // _COMPILEUTILITIES_H_
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,338 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// DistillObjects.cpp
//
// called Object Distiller in the asm code
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
bool DistillSetup_Enter(unsigned short value)
{
if (g_pCompilerData->dis_ptr == distiller_limit)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_odo];
return false;
}
g_pCompilerData->dis[g_pCompilerData->dis_ptr++] = value;
return true;
}
// create a table of all objects with their offsets and sub objects
// each entry contains:
// id, offset, number sub objects, [sub object ids]
bool DistillSetup_Record(short id, unsigned short offset, unsigned short& subObjectId)
{
if (!DistillSetup_Enter(id))
{
return false;
}
if (!DistillSetup_Enter(offset))
{
return false;
}
unsigned short numSubObjects = (unsigned short)(g_pCompilerData->obj[offset+3]);
if (!DistillSetup_Enter(numSubObjects))
{
return false;
}
if (numSubObjects > 0)
{
short startingSubObjectId = subObjectId;
for (short i = 0; i < numSubObjects; i++)
{
if (!DistillSetup_Enter(subObjectId++))
{
return false;
}
}
unsigned short nextSubObjects = (unsigned short)(g_pCompilerData->obj[offset+2]);
for (short i = 0; i < numSubObjects; i++)
{
unsigned short offsetAdjust = *((unsigned short*)&(g_pCompilerData->obj[offset + ((nextSubObjects + i) * 4)]));
if (!DistillSetup_Record(startingSubObjectId + i, offset + offsetAdjust, subObjectId))
{
return false;
}
}
}
return true;
}
bool DistillSetup()
{
g_pCompilerData->dis_ptr = 0;
unsigned short subObjectId = 1;
if (!DistillSetup_Record(0, 0, subObjectId))
{
return false;
}
// Clear all the objects table of offsets to their sub objects
// this needs to be done so that objects will binary compare with each other properly
// these offsets get replaced in the reconnect step
int disPtr = 0;
while (disPtr < g_pCompilerData->dis_ptr)
{
// do we have sub objects?
unsigned short numSubObjects = g_pCompilerData->dis[disPtr + 2];
if (numSubObjects > 0)
{
unsigned short offset = g_pCompilerData->dis[disPtr + 1];
disPtr += numSubObjects;
unsigned short offsetAdjust = (unsigned short)(g_pCompilerData->obj[offset + 2]);
unsigned char* pObj = &(g_pCompilerData->obj[offset + (offsetAdjust * 4)]);
for (int i = 0; i < numSubObjects; i++)
{
*((unsigned short*)&pObj[0]) = 0;
pObj += 4;
}
}
disPtr += 3;
}
return true;
}
// update all objects of the given id to the new id
// also flags them as "distilled" with the upper bit being on
void DistillEliminate_Update(unsigned short objectId, int newDisPtr)
{
int disPtr = 0;
while (disPtr < g_pCompilerData->dis_ptr)
{
disPtr += 3;
unsigned short numSubObjects = g_pCompilerData->dis[disPtr - 1];
if (numSubObjects > 0)
{
for (int i = 0; i < numSubObjects; i++)
{
if ((g_pCompilerData->dis[disPtr] & 0x7FFF) == objectId)
{
g_pCompilerData->dis[disPtr] = (g_pCompilerData->dis[newDisPtr] | 0x8000);
}
disPtr++;
}
}
}
}
void DistillEliminate()
{
int disPtr = 0;
while (disPtr < g_pCompilerData->dis_ptr)
{
unsigned short numSubObjects = g_pCompilerData->dis[disPtr + 2];
if (numSubObjects > 0)
{
int i;
for (i = 0; i < numSubObjects; i++)
{
if ((g_pCompilerData->dis[disPtr + 3 + i] & 0x8000) == 0)
{
break;
}
}
if (i < numSubObjects)
{
// point to next object record
disPtr += (3 + numSubObjects);
continue;
}
}
// search for any matching objects
int newDisPtr = disPtr;
// point to next object record
newDisPtr += (3 + numSubObjects);
bool bRestart = false;
while (newDisPtr < g_pCompilerData->dis_ptr)
{
unsigned short newNumSubObjects = g_pCompilerData->dis[newDisPtr + 2];
if (numSubObjects != newNumSubObjects)
{
// point to next object record
newDisPtr += (3 + newNumSubObjects);
continue;
}
if (newNumSubObjects > 0)
{
int i;
for (i = 0; i < newNumSubObjects; i++)
{
if (g_pCompilerData->dis[disPtr + 3 + i] != g_pCompilerData->dis[newDisPtr + 3 + i])
{
break;
}
}
if (i < newNumSubObjects)
{
// point to next object record
newDisPtr += (3 + newNumSubObjects);
continue;
}
}
// compare the object binaries
unsigned char* pObj = &(g_pCompilerData->obj[g_pCompilerData->dis[disPtr+1]]);
unsigned short objLength = *((unsigned short*)pObj);
if (memcmp(pObj, &(g_pCompilerData->obj[g_pCompilerData->dis[newDisPtr+1]]), (size_t)objLength) != 0)
{
// point to next object record
newDisPtr += (3 + newNumSubObjects);
continue;
}
// the objects match, so update all related sub-object id's
DistillEliminate_Update(g_pCompilerData->dis[disPtr], newDisPtr);
DistillEliminate_Update(g_pCompilerData->dis[newDisPtr], newDisPtr);
// remove redundant object record from list
g_pCompilerData->dis_ptr -= (3 + numSubObjects);
memmove(&g_pCompilerData->dis[disPtr], &g_pCompilerData->dis[disPtr + (3 + numSubObjects)], (g_pCompilerData->dis_ptr - disPtr) * 2);
// restart elimination from beginning
bRestart = true;
}
if (bRestart)
{
disPtr = 0;
}
else
{
// point to next object record
disPtr += (3 + numSubObjects);
continue;
}
}
}
static unsigned char s_rebuildBuffer[min_obj_limit];
void DistillRebuild()
{
int disPtr = 0;
unsigned short rebuildPtr = 0;
while (disPtr < g_pCompilerData->dis_ptr)
{
// copy the object from obj into the rebuild buffer
unsigned char* pObj = &(g_pCompilerData->obj[g_pCompilerData->dis[disPtr + 1]]);
unsigned short objLength = *((unsigned short*)pObj);
memcpy(&(s_rebuildBuffer[rebuildPtr]), pObj, (size_t)objLength);
// fixup the distiller record
g_pCompilerData->dis[disPtr+1] = rebuildPtr;
rebuildPtr += objLength;
// point to the next object record
disPtr += (3 + g_pCompilerData->dis[disPtr + 2]);
}
// copy the rebuilt data back into obj
g_pCompilerData->obj_ptr = rebuildPtr;
memcpy(&g_pCompilerData->obj[0], &s_rebuildBuffer[0], (size_t)rebuildPtr);
}
void DistillReconnect(int disPtr = 0)
{
unsigned short numSubObjects = g_pCompilerData->dis[disPtr + 2];
if (numSubObjects > 0)
{
// this objects offset in the obj
unsigned short objectOffset = g_pCompilerData->dis[disPtr + 1];
// the offset (number of longs) to the sub-object offset list within this obj
unsigned char subObjectOffsetListPtr = g_pCompilerData->obj[objectOffset + 2];
// pointer to the sub-object offset list for this obj
unsigned short* pSubObjectOffsetList = (unsigned short*)&(g_pCompilerData->obj[objectOffset + (subObjectOffsetListPtr * 4)]);
for (int i = 0; i < numSubObjects; i++)
{
unsigned short subObjectId = g_pCompilerData->dis[disPtr + 3 + i] & 0x7FFF;
// find offset of sub-object
int scanDisPtr = 0;
for (;;)
{
if (g_pCompilerData->dis[scanDisPtr] == subObjectId)
{
break;
}
scanDisPtr += (3 + g_pCompilerData->dis[scanDisPtr + 2]);
}
// enter relative offset of sub-object
pSubObjectOffsetList[i*2] = g_pCompilerData->dis[scanDisPtr + 1] - objectOffset;
// call recursively to reconnect and sub-objects' sub-objects
DistillReconnect(scanDisPtr);
}
}
}
bool DistillObjects()
{
int saved_obj_ptr = g_pCompilerData->obj_ptr;
if (!DistillSetup())
{
return false;
}
DistillEliminate();
DistillRebuild();
DistillReconnect();
g_pCompilerData->distilled_longs = (saved_obj_ptr - g_pCompilerData->obj_ptr) >> 2;
char tempStr[64];
sprintf(tempStr, "\rDistilled longs: %d", g_pCompilerData->distilled_longs);
if (!PrintString(tempStr))
{
return false;
}
return PrintChr(13);
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,810 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// Elementizer.cpp
//
#include <string.h>
#include "PropellerCompilerInternal.h"
#include "Elementizer.h"
#include "SymbolEngine.h"
#include "ErrorStrings.h"
#include "Utilities.h"
// private
// set elementizer data from the currently set symbol entry
void Elementizer::SetFromSymbolEntry()
{
if (m_pSymbolEntry)
{
m_type = m_pSymbolEntry->m_data.type;
m_value = m_pSymbolEntry->m_data.value;
m_value_2 = m_pSymbolEntry->m_data.value_2;
if (m_pSymbolEntry->m_data.dual)
{
m_dual = true;
m_asm = m_pSymbolEntry->m_data.operator_type_or_asm;
}
else
{
m_dual = false;
m_opType = m_pSymbolEntry->m_data.operator_type_or_asm;
// fixup for AND and OR to have asm also
if (m_type == type_binary && m_opType == op_log_and)
{
m_asm = 0x18 + 0x40;
}
if (m_type == type_binary && m_opType == op_log_or)
{
m_asm = 0x1A + 0x40;
}
}
}
else
{
m_type = 0;
m_value = 0;
m_value_2 = 0;
m_asm = -1;
m_opType = -1;
}
}
// public
// reset to start of source
void Elementizer::Reset()
{
m_sourceOffset = 0;
m_sourceFlags = 0;
}
// get the next element in source, returns true no error, bEof will be set to true if eof is hit
bool Elementizer::GetNext(bool& bEof)
{
// update back data
m_backOffsets[m_backIndex&0x03] = m_sourceOffset;
m_backFlags[m_backIndex&0x03] = m_sourceFlags;
m_backIndex++;
// default to type_undefined
m_type = 0;
m_value = 0;
m_value_2 = 0;
m_asm = -1;
m_opType = -1;
m_pSymbolEntry = 0;
// no error, and not end of file
int error = error_none;
bEof = false;
bool bDocComment = false;
int constantBase = 0;
// setup source and symbol pointers
char* pSource = m_pCompilerData->source;
int sourceStart = m_sourceOffset;
m_currentSymbol[0] = 0;
int symbolOffset = 0;
bool bConstantOverflow = false;
for (;;)
{
char currentChar = pSource[m_sourceOffset++];
// parse
if (constantBase > 0)
{
// this handles reading in a constant of base 2, 4, 10, or 16
// the constantBase value is set based on prefix characters handled below
if (currentChar == '_')
{
// skip over _'s
continue;
}
char digitValue;
if (!CheckDigit(currentChar, digitValue, (char)constantBase))
{
char notUsed;
char nextChar = pSource[m_sourceOffset];
bool bNextCharDigit = CheckDigit(nextChar, notUsed, (char)constantBase);
if ((constantBase == 10 &&
(currentChar == '.' && bNextCharDigit)) ||
currentChar == 'e' || currentChar == 'E')
{
// handle float
bConstantOverflow = false;
m_sourceOffset = sourceStart;
if (GetFloat(pSource, m_sourceOffset, m_value))
{
m_sourceOffset--; // back up to point at last digit
m_type = type_con_float;
}
else
{
error = error_fpcmbw;
}
}
else
{
// done with this constant
m_sourceOffset--; // back up to point at last digit
m_type = type_con;
}
constantBase = 0;
break;
}
else
{
// multiply accumulate the constant
unsigned int oldValue = m_value;
m_value *= constantBase;
// check for overflow
if (((unsigned int)m_value / constantBase) != oldValue)
{
bConstantOverflow = true;
}
m_value += digitValue;
}
continue;
}
else if (m_sourceFlags != 0)
{
// old string? (continue parsing a string)
// for strings, m_sourceFlags will start out 0, and then cycle between 1 and 2 for
// each character of the string, when it is 1, a type_comma is returned, when it is
// 2 the next character is returned
// return a comma element between each character of the string
if (m_sourceFlags == 1)
{
m_sourceFlags++;
m_sourceOffset--;
m_type = type_comma;
break;
}
// reset flag
m_sourceFlags = 0;
// check for errors
if (currentChar == '\"')
{
error = error_es;
break;
}
else if (currentChar == 0)
{
m_sourceOffset--; // back up from eof
error = error_eatq;
break;
}
else if (currentChar == 13)
{
error = error_eatq;
break;
}
// return the character
m_value = currentChar;
// check the next character, if it's not a " then setup so the next
// call returns a type_comma, if it is a ", then we are done with this string
// and we leave the offset pointing after the "
currentChar = pSource[m_sourceOffset++];
if (currentChar != '\"')
{
m_sourceOffset--;
m_sourceFlags++;
}
// return the character constant
m_type = type_con;
break;
}
else if (currentChar == '\"')
{
// new string (start parsing a string)
// we got here because m_sourceFlags was 0 and the character is a "
// get first character of string
currentChar = pSource[m_sourceOffset++];
// check for errors
if (currentChar == '\"')
{
error = error_es;
break;
}
else if (currentChar == 0)
{
m_sourceOffset--; // back up from eof
error = error_eatq;
break;
}
else if (currentChar == 13)
{
error = error_eatq;
break;
}
// return the character in value
m_value = currentChar & 0x000000FF;
// check the next character, it's it's not a " then setup so the next
// call returns a type_comma, if it is a " then it means it's a one character
// string and we leave the offset pointing after the "
currentChar = pSource[m_sourceOffset++];
if (currentChar != '\"')
{
m_sourceOffset--; // back up, so this character will be read after the type_comma
m_sourceFlags = 1; // cause the next call to return a type_comma
}
// return the character constant
m_type = type_con;
break;
}
else if (currentChar == 0)
{
// eof
m_type = type_end;
bEof = true;
m_sourceOffset--;
sourceStart = m_sourceOffset;
break;
}
else if (currentChar == 13)
{
// eol
m_type = type_end;
break;
}
else if (currentChar <= ' ')
{
// space or tab?
sourceStart = m_sourceOffset;
continue;
}
else if (currentChar == '\'')
{
// comment
// read until end of line or file, handle doc comment
if (pSource[m_sourceOffset] == '\'')
{
m_sourceOffset++; // skip over second '
bDocComment = true;
g_pCompilerData->doc_flag = true;
}
for (;;)
{
currentChar = pSource[m_sourceOffset++];
if (currentChar == 0)
{
m_sourceOffset--; // back up from eof
m_type = type_end;
bEof = true;
break;
}
if (bDocComment)
{
DocPrint(currentChar);
}
if (currentChar == 13)
{
m_type = type_end;
break;
}
}
break;
}
else if (currentChar == '{')
{
// brace comment
// read the whole comment, handling doc comments as needed
int braceCommentLevel = 1;
if (pSource[m_sourceOffset] == '{')
{
m_sourceOffset++; // skip over second {
bDocComment = true;
g_pCompilerData->doc_flag = true;
if (pSource[m_sourceOffset] == 13)
{
m_sourceOffset++; // skip over end if present
}
}
for (;;)
{
currentChar = pSource[m_sourceOffset++];
if (currentChar == 0)
{
if (bDocComment)
{
error = error_erbb;
}
else
{
error = error_erb;
}
m_sourceOffset--; // back up from eof
sourceStart = m_sourceOffset;
break;
}
else if (!bDocComment && currentChar == '{')
{
braceCommentLevel++;
}
else if (currentChar == '}')
{
if (bDocComment && pSource[m_sourceOffset] == '}')
{
m_sourceOffset++; // skip over second }
break;
}
else if (!bDocComment)
{
braceCommentLevel--;
if (braceCommentLevel < 1)
{
break;
}
}
}
else if (bDocComment)
{
DocPrint(currentChar);
}
}
if (error == error_none)
{
sourceStart = m_sourceOffset;
continue;
}
else
{
break;
}
}
else if (currentChar == '}')
{
// unmatched brace comment end
error = error_bmbpbb;
break;
}
else if (currentChar == '%')
{
// binary
currentChar = pSource[m_sourceOffset++];
char temp;
if (currentChar == '%')
{
// double binary
currentChar = pSource[m_sourceOffset++];
if (!CheckDigit(currentChar, temp, 4))
{
error = error_idbn;
break;
}
constantBase = 4;
}
else
{
if (!CheckDigit(currentChar, temp, 2))
{
error = error_idbn;
break;
}
constantBase = 2;
}
m_sourceOffset--; // back up to first digit
// constantBase is now set, so loop back around to read in the constant
continue;
}
else if (currentChar == '$')
{
// hex
currentChar = pSource[m_sourceOffset++];
char temp;
if (!CheckDigit(currentChar, temp, 16))
{
m_sourceOffset--;
m_type = type_asm_org;
break;
}
constantBase = 16;
m_sourceOffset--; // back up to first digit
// constantBase is now set, so loop back around to read in the constant
continue;
}
else if (currentChar >= '0' && currentChar <= '9')
{
// dec
constantBase = 10;
m_sourceOffset--; // back up to first digit
// constantBase is now set, so loop back around to read in the constant
continue;
}
else
{
// symbol
currentChar = Uppercase(currentChar);
if (CheckWordChar(currentChar))
{
// do word symbol
while(CheckWordChar(currentChar) && symbolOffset <= symbol_limit)
{
m_currentSymbol[symbolOffset++] = currentChar;
currentChar = Uppercase(pSource[m_sourceOffset++]);
}
if (symbolOffset > symbol_limit)
{
error = error_sexc;
}
else
{
// back up so we point at last char of symbol
m_sourceOffset--;
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
}
}
else
{
// try non-word symbol (one or two char operators)
m_currentSymbol[symbolOffset++] = currentChar;
currentChar = pSource[m_sourceOffset++];
bool bDoOneChar = false;
bool bDoTwoChar = false;
// if the next char is not whitespace or eol
if (currentChar > ' ')
{
// three char symbol
// assign second char into symbol
m_currentSymbol[symbolOffset++] = currentChar;
// read third char into symbol
m_currentSymbol[symbolOffset++] = pSource[m_sourceOffset++];
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
if (m_pSymbolEntry == 0)
{
bDoTwoChar = true;
symbolOffset--;
}
}
if (bDoTwoChar)
{
// two char symbol
// back up so we point at last char of symbol
m_sourceOffset--;
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
if (m_pSymbolEntry == 0)
{
bDoOneChar = true;
symbolOffset--;
}
}
if (bDoOneChar || currentChar <= ' ')
{
// one char symbol
// back up so we point at last char of symbol
m_sourceOffset--;
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
if (m_pSymbolEntry == 0)
{
error = error_uc;
}
}
}
break;
}
}
if (bConstantOverflow)
{
error = error_ce32b;
}
// update pointers
m_pCompilerData->source_start = sourceStart;
m_pCompilerData->source_finish = m_sourceOffset;
// if we got a symbol, then set the type, value, etc.
if (m_type == 0 && m_pSymbolEntry)
{
SetFromSymbolEntry();
}
if (error != error_none)
{
m_pCompilerData->error = true;
m_pCompilerData->error_msg = g_pErrorStrings[error];
return false;
}
return true;
}
// if the next element is type, then return true, else false, retains value
bool Elementizer::GetElement(int type)
{
int value = m_value; // save current value
bool bEof = false;
GetNext(bEof);
if (GetType() != type)
{
m_pCompilerData->error = true;
int errorNum = 0;
switch (type)
{
case type_left: errorNum = error_eleft; break;
case type_right: errorNum = error_eright; break;
case type_rightb: errorNum = error_erightb; break;
case type_comma: errorNum = error_ecomma; break;
case type_pound: errorNum = error_epound; break;
case type_colon: errorNum = error_ecolon; break;
case type_dot: errorNum = error_edot; break;
case type_sub: errorNum = error_easn; break;
case type_end: errorNum = error_eeol; break;
}
m_pCompilerData->error_msg = g_pErrorStrings[errorNum];
return false;
}
m_value = value; // restore saved value
return true;
}
// check if next element is of the given type, if so return true, if not, backup and return false
bool Elementizer::CheckElement(int type)
{
bool bEof = false;
GetNext(bEof);
if (GetType() == type)
{
return true;
}
Backup();
return false;
}
// scan for the next block element of type, returns true if no error, , bEof will be set to true if eof is hit
bool Elementizer::GetNextBlock(int type, bool& bEof)
{
bool bFound = false;
while(bFound == false)
{
if (GetNext(bEof) == false || bEof == true)
{
break;
}
if (GetType() == type_block && GetValue() == type)
{
if (GetColumn() != 1)
{
m_pCompilerData->error = true;
m_pCompilerData->error_msg = g_pErrorStrings[error_bdmbifc];
return false;
}
bFound = true;
}
}
// if we found the block or we hit eof, then we got no error so return true
return (bFound || bEof);
}
// returns column of most recent Element gotten
int Elementizer::GetColumn()
{
char* pSource = m_pCompilerData->source;
int sourceStart = m_pCompilerData->source_start;
if (sourceStart == 0)
{
// we are at the start of the source, so return 1
return 1;
}
// back up until we hit a CR character
while(pSource[sourceStart] != 13 && sourceStart > 0)
{
sourceStart--;
}
// advance forward one, (off of the CR)
sourceStart++;
if (sourceStart == m_pCompilerData->source_start)
{
// we are at the start of the line, so return 1
return 1;
}
// adjust source pointer to start of line
pSource += sourceStart;
// adjust sourceStart such that it is how many characters we backed up
sourceStart = m_pCompilerData->source_start - sourceStart;
// count the characters we backed up over, accounting for tabs (tabs are 8 chars)
int column = 0;
for (int i = 0; i < sourceStart; i++)
{
if (pSource[i] == 9)
{
column |= 7;
}
column++;
}
return column + 1;
}
int Elementizer::GetCurrentLineNumber(int &offsetToStartOfLine, int& offsetToEndOfLine)
{
int lineCount = 1;
char* pSource = m_pCompilerData->source;
int scanEnd = m_pCompilerData->source_start;
offsetToStartOfLine = -1;
while (scanEnd > 0)
{
if (pSource[--scanEnd] == 13)
{
if (offsetToStartOfLine == -1)
{
offsetToStartOfLine = scanEnd+1;
}
lineCount++;
}
}
if (offsetToStartOfLine == -1)
{
offsetToStartOfLine = 0;
}
scanEnd = m_pCompilerData->source_start;
while (pSource[scanEnd] != 0)
{
if (pSource[scanEnd] == 13 || pSource[scanEnd] == 0)
{
break;
}
scanEnd++;
}
offsetToEndOfLine = scanEnd;
return lineCount;
}
// backup to the previous element
void Elementizer::Backup()
{
m_backIndex--;
m_sourceOffset = m_backOffsets[m_backIndex&0x03];
m_sourceFlags = m_backFlags[m_backIndex&0x03];
}
void Elementizer::ObjConToCon()
{
m_type -= (type_objcon - type_con);
}
void Elementizer::DatResToLong()
{
if (m_type == type_dat_long_res)
{
m_type = type_dat_long;
}
}
bool Elementizer::SubToNeg()
{
if (m_type == type_binary && m_opType == op_sub)
{
m_type = type_unary;
m_opType = op_neg;
m_value = 0;
m_value_2 = 0;
return true;
}
return false;
}
bool Elementizer::NegConToCon()
{
if (m_type == type_binary && m_opType == op_sub)
{
int savedValue = m_value;
bool bEof = false;
if (!GetNext(bEof))
{
return false;
}
if (m_type == type_con)
{
m_value = -m_value;
}
else if (m_type == type_con_float)
{
m_value |= 0x80000000;
}
else
{
Backup();
m_type = type_binary;
m_asm = -1;
m_opType = op_sub;
m_value = savedValue;
}
}
return true;
}
bool Elementizer::FindSymbol(const char* symbol)
{
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(symbol);
SetFromSymbolEntry();
return true;
}
void Elementizer::BackupSymbol()
{
strcpy(m_pCompilerData->symbolBackup, m_currentSymbol);
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,122 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// Elementizer.h
//
#ifndef _ELEMENTIZER_H_
#define _ELEMENTIZER_H_
struct CompilerDataInternal;
class SymbolEngine;
class SymbolTableEntry;
const int state_stack_limit = 32;
class Elementizer
{
CompilerDataInternal* m_pCompilerData;
SymbolEngine* m_pSymbolEngine;
int m_sourceOffset;
unsigned char m_sourceFlags;
SymbolTableEntry* m_pSymbolEntry;
int m_type;
int m_value;
int m_value_2;
int m_opType;
int m_asm;
bool m_dual;
unsigned char m_backIndex;
int m_backOffsets[4];
unsigned char m_backFlags[4];
char m_currentSymbol[symbol_limit+2];
void SetFromSymbolEntry();
public:
Elementizer(CompilerDataInternal* pCompilerData, SymbolEngine* pSymbolEngine)
: m_pCompilerData(pCompilerData)
, m_pSymbolEngine(pSymbolEngine)
, m_sourceOffset(0)
, m_sourceFlags(0)
, m_backIndex(0)
{
for(int i = 0; i < 4; i++)
{
m_backOffsets[i] = 0;
m_backFlags[0] = 0;
}
}
void Reset(); // reset to start of source
bool GetNext(bool& bEof); // get the next element in source, returns true no error, bEof will be set to true if eof is hit
bool GetElement(int type); // if the next element is type, then return true, else false, retains value
bool CheckElement(int type); // check if next element is of the given type, if so return true, if not, backup and return false
bool GetNextBlock(int type, bool& bEof); // scan for the next block element of type, returns true if no error, , bEof will be set to true if eof is hit
bool FindSymbol(const char* symbol); // lookup a symbol in the symbol table and set it as the current element
void Backup(); // backup to the previous element
void BackupSymbol(); // copy the current symbol into g_pCompilerData->symbolBackup
int GetColumn(); // returns column of the element pointed to by g_pCompilerData->source_start
int GetSourcePtr() // used to save the current source pointer so it can be put back
{
return m_sourceOffset;
}
void SetSourcePtr(int value) // used to set the source pointer back to a previously saved value
{
m_sourceOffset = value;
}
int GetType() { return m_type; } // symbol's type
int GetValue() { return m_value; } // only valid if m_type != type_undefined
int GetValue2() { return m_value_2; } // only valid if m_type != type_undefined
int GetOpType() { return m_opType; } // only valid for operator symbols
int GetAsm() { return m_asm; } // only valid for dual symbols + op_log_and & op_log_or
bool IsDual() { return m_dual; } // true if is a dual symbol
char* GetCurrentSymbol() // returns the string for the symbol
{
return &(m_currentSymbol[0]);
}
int GetCurrentLineNumber(int &offsetToStartOfLine, int& offsetToEndOfLine);
bool SubToNeg(); // convert a sub to a neg
void ObjConToCon(); // convert type_objcon_xx to type_con_xx
void DatResToLong(); // convert type_dat_long_res to type_dat_long
bool NegConToCon(); // convert -constant to constant
};
#endif // _ELEMENTIZER_H_
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,154 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// ErrorStrings.cpp
//
const char* g_pErrorStrings[] =
{
"Address is not long",
"Address is out of range",
"\"}\" must be preceeded by \"{\" to form a comment",
"Block designator must be in first column",
"Blocknest stack overflow",
"Cannot compute square root of negative floating-point number",
"Constant exceeds 32 bits",
"_CLKFREQ or _XINFREQ must be specified",
"CALL symbol must not exceed 252 characters",
"_CLKFREQ/_XINFREQ not allowed with RCFAST/RCSLOW",
"_CLKFREQ/_XINFREQ specified without _CLKMODE",
"Divide by zero",
"Destination register cannot exceed $1FF",
"Expected an assembly effect or end of line",
"Expected an assembly effect",
"Expected an assembly instruction",
"Expected a binary operator or \")\"",
"Expected a constant name",
"Expected a constant, unary operator, or \"(\"",
"Expected a DAT symbol",
"Expected an expression term",
"Expected an instruction or variable",
"Expected a local symbol",
"Expected a memory variable after \"@\"",
"Expected a subroutine name",
"Expected a subroutine or object name",
"Expected a terminating quote",
"Expected a unique object name",
"Expected a variable",
"Expected a unique constant name or \"#\"",
"Expected a unique name, BYTE, WORD, LONG, or assembly instruction",
"Expected a unique parameter name",
"Expected a unique result name",
"Expected a unique subroutine name",
"Expected a unique variable name",
"Expected BYTE, WORD, or LONG",
"Expected \",\" or end of line",
"Expected \":\"",
"Expected \",\"",
"Expected \",\" or \")\"",
"Either _CLKFREQ or _XINFREQ must be specified, but not both",
"Expected \".\"",
"Expected end of line",
"Expected \"=\" \"[\" \",\" or end of line",
"Expected FROM",
"Expression is too complex",
"Expected \"(\"",
"Expected \"[\"",
"Expected PRECOMPILE or ARCHIVE",
"Expected \"|\" or end of line",
"Expected \"#\"",
"Expected \"}\"",
"Expected \"}}\"",
"Expected \")\"",
"Expected \"]\"",
"Empty string",
"Expected STEP or end of line",
"Expected TO",
"Filename too long",
"Floating-point constant must be within +/- 3.4e+38",
"Floating-point not allowed in integer expression",
"Floating-point overflow",
"Invalid binary number",
"Invalid _CLKMODE specified",
"Invalid double-binary number",
"Internal DAT file not found",
"Invalid filename character",
"Invalid filename, use \"FilenameInQuotes\"",
"Integer not allowed in floating-point expression",
"Internal",
"Integer operator not allowed in floating-point expression",
"Limit of 64 cases exceeded",
"Limit of 8 nested blocks exceeded",
"Limit of 32 unique objects exceeded",
"Limit of 32 unique DAT files exceeded",
"Limit of 32 unique PRECOMPILE files exceeded",
"Limit of 32 unique ARCHIVE files exceeded",
"List is too large",
"Limit of 1,048,576 DAT symbols exceeded",
"Limit of 16 ELSEIFs exceeded",
"Limit of 4096 local variables exceeded",
"Limit of 15 parameters exceeded",
"Limit of 256 subroutines + objects exceeded",
"Memory instructions cannot use WR/NR",
"No cases encountered",
"No PUB routines found",
"Object count must be from 1 to 255",
"Object distiller overflow",
"Origin exceeds FIT limit",
"Object exceeds 128k (before distilling)",
"Origin exceeds $1F0 limit",
"\"$\" is not allowed here",
"OTHER must be last case",
"PUB/CON list overflow",
"?_RET address is not long",
"?_RET address is out of range",
"Register is not allowed here",
"RES is not allowed in ORGX mode",
"_STACK and _FREE must sum to under 8k",
"Symbols _CLKMODE, _CLKFREQ, _XINFREQ can only be used as integer constants",
"String characters must range from 1 to 255",
"Symbol _DEBUG can only be used as an integer constant",
"Symbol exceeds 256 characters",
"Symbol is already defined",
"STRING not allowed here",
"Size override must be larger",
"Size override must be smaller",
"Source register/constant cannot exceed $1FF",
"Symbols _STACK and _FREE can only be used as integer constants",
"Symbol table is full",
"This instruction is only allowed within a REPEAT block",
"Too many string constants",
"Too many string constant characters",
"Too much variable space is declared",
"Unrecognized character",
"Undefined ?_RET symbol",
"Undefined symbol",
"Variable needs an operator"
};
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,162 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// ErrorStrings.h
//
#ifndef _ERROR_STRINGS_H_
#define _ERROR_STRINGS_H_
enum errorType
{
error_none = -1,
error_ainl = 0,
error_aioor,
error_bmbpbb,
error_bdmbifc,
error_bnso,
error_ccsronfp,
error_ce32b,
error_coxmbs,
error_csmnexc,
error_cxnawrc,
error_cxswcm,
error_dbz,
error_drcex,
error_eaaeoeol,
error_eaasme,
error_eaasmi,
error_eaboor,
error_eacn,
error_eacuool,
error_eads,
error_eaet,
error_eaiov,
error_eals,
error_eamvaa,
error_easn,
error_easoon,
error_eatq,
error_eauon,
error_eav,
error_eaucnop,
error_eaunbwlo,
error_eaupn,
error_eaurn,
error_eausn,
error_eauvn,
error_ebwol,
error_ecoeol,
error_ecolon,
error_ecomma,
error_ecor,
error_ecoxmbs,
error_edot,
error_eeol,
error_eelcoeol,
error_efrom,
error_eitc,
error_eleft,
error_eleftb,
error_epoa,
error_epoeol,
error_epound,
error_erb,
error_erbb,
error_eright,
error_erightb,
error_es,
error_esoeol,
error_eto,
error_ftl,
error_fpcmbw,
error_fpnaiie,
error_fpo,
error_ibn,
error_icms,
error_idbn,
error_idfnf,
error_ifc,
error_ifufiq,
error_inaifpe,
error_internal,
error_ionaifpe,
error_loxce,
error_loxnbe,
error_loxuoe,
error_loxudfe,
error_loxupfe,
error_loxuafe,
error_litl,
error_loxdse,
error_loxee,
error_loxlve,
error_loxpe,
error_loxspoe,
error_micuwn,
error_nce,
error_nprf,
error_ocmbf1tx,
error_odo,
error_oefl,
error_oex,
error_oexl,
error_oinah,
error_omblc,
error_pclo,
error_rainl,
error_raioor,
error_rinah,
error_rinaiom,
error_safms,
error_sccx,
error_scmr,
error_sdcobu,
error_sexc,
error_siad,
error_snah,
error_sombl,
error_sombs,
error_srccex,
error_ssaf,
error_stif,
error_tioawarb,
error_tmsc,
error_tmscc,
error_tmvsid,
error_uc,
error_urs,
error_us,
error_vnao
};
extern const char* g_pErrorStrings[];
#endif // _ERROR_STRINGS_H_
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,977 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// ExpressionResolver.cpp
//
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
//bool GetTryValue(bool bMustResolve, bool bInteger, bool bOperandMode = false); // declared in Utilities.h
//////////////////////////////////////////
// declarations of internal functions
//
void ResolveExpression();
void ResolveSubExpression(int precedence);
void GetTerm(int& precedence);
bool CheckUndefined(bool& bUndefined);
bool CheckDat();
bool CheckConstant(bool& bConstant);
bool GetObjSymbol(int type, char id);
bool PreviewOp();
bool PerformPush();
bool PerformBinary();
bool PerformOp();
//////////////////////////////////////////
// exported functions
//
// only valid after calling GetTryValue() with bMustResolve set to true and it returned true
int GetResult()
{
return g_pCompilerData->mathStack[g_pCompilerData->mathCurrent - 1];
}
// if this succeeds and bMustResolve it true then, the result is in g_pCompilerData->mathStack[g_pCompilerData->mathCurrent-1]
bool GetTryValue(bool bMustResolve, bool bInteger, bool bOperandMode)
{
g_pCompilerData->intMode = bInteger ? 1 : 0;
g_pCompilerData->bMustResolve = bMustResolve;
g_pCompilerData->bOperandMode = bOperandMode;
g_pCompilerData->mathCurrent = 0;
g_pCompilerData->bUndefined = false;
g_pCompilerData->currentOp = 0;
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
int save_start = g_pCompilerData->source_start;
g_pElementizer->Backup();
// results are put into g_pCompilerData
ResolveExpression();
if (g_pCompilerData->error)
{
return false;
}
g_pElementizer->Backup();
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
g_pCompilerData->source_start = save_start;
return true;
}
//////////////////////////////////////////
// internal function definitions
//
void ResolveExpression()
{
g_pCompilerData->precedence = 11;
ResolveSubExpression(g_pCompilerData->precedence - 1);
}
void ResolveSubExpression(int precedence)
{
if (precedence < 0)
{
GetTerm(precedence);
}
else
{
ResolveSubExpression(precedence - 1);
}
if (g_pCompilerData->error)
{
return;
}
bool bEof = false;
while (!bEof)
{
if (!g_pElementizer->GetNext(bEof))
{
return;
}
if (g_pElementizer->GetType() != type_binary)
{
g_pElementizer->Backup();
return;
}
if (!PreviewOp())
{
return;
}
if (precedence != g_pElementizer->GetValue())
{
g_pElementizer->Backup();
return;
}
g_pCompilerData->savedOp[g_pCompilerData->currentOp] = g_pElementizer->GetOpType();
g_pCompilerData->currentOp++;
int save_start = g_pCompilerData->source_start;
int save_finish = g_pCompilerData->source_finish;
ResolveSubExpression(precedence - 1);
if (g_pCompilerData->error)
{
return;
}
g_pCompilerData->source_start = save_start;
g_pCompilerData->source_finish = save_finish;
if (!PerformBinary())
{
return;
}
g_pCompilerData->currentOp--;
}
}
void GetTerm(int& precedence)
{
bool bEof = false;
// skip over any leading +'s
do
{
g_pElementizer->GetNext(bEof);
if (g_pElementizer->GetType() == type_binary && g_pElementizer->GetOpType() == op_add)
{
continue;
}
break;
} while (!bEof);
bool bConstant = false;
if (!CheckConstant(bConstant))
{
if (g_pCompilerData->error)
{
return;
}
}
if (bConstant)
{
PerformPush();
return;
}
if (g_pElementizer->SubToNeg())
{
precedence = 0;
}
if (g_pElementizer->GetType() == type_unary)
{
if (!PreviewOp())
{
return;
}
precedence = g_pElementizer->GetValue(); // for unary types, value = precedence
int save_start = g_pCompilerData->source_start;
int save_finish = g_pCompilerData->source_finish;
g_pCompilerData->savedOp[g_pCompilerData->currentOp] = g_pElementizer->GetOpType();
g_pCompilerData->currentOp++;
ResolveSubExpression(precedence - 1);
if (g_pCompilerData->error)
{
return;
}
g_pCompilerData->source_start = save_start;
g_pCompilerData->source_finish = save_finish;
if (!PerformOp())
{
return;
}
g_pCompilerData->currentOp--;
}
else if (g_pElementizer->GetType() == type_left)
{
ResolveExpression();
if (!g_pElementizer->GetElement(type_right))
{
return;
}
}
else if (g_pCompilerData->bMustResolve)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eacuool];
// when we return from here, the calling code will return due to error = true
}
}
bool CheckUndefined(bool& bUndefined)
{
if (g_pElementizer->GetType() == type_undefined)
{
g_pCompilerData->bUndefined = bUndefined = true;
int save_start = g_pCompilerData->source_start;
int save_finish = g_pCompilerData->source_finish;
if(g_pElementizer->CheckElement(type_pound))
{
int length = 0;
if (!GetSymbol(&length))
{
return false;
}
if (length == 0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eacn];
return false;
}
}
g_pCompilerData->source_start = save_start;
g_pCompilerData->source_finish = save_finish;
if (g_pCompilerData->bMustResolve)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_us];
return false;
}
}
else
{
bUndefined = false;
}
return true;
}
bool CheckDat()
{
if (g_pCompilerData->bOperandMode)
{
g_pElementizer->DatResToLong();
}
if ((g_pElementizer->GetType() == type_dat_byte) ||
(g_pElementizer->GetType() == type_dat_word) ||
(g_pElementizer->GetType() == type_dat_long))
{
return true;
}
return false;
}
bool CheckConstant(bool& bConstant)
{
bConstant = true;
if (g_pElementizer->GetType() == type_con)
{
if (g_pCompilerData->intMode == 2)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_fpnaiie];
return false;
}
else
{
g_pCompilerData->intMode = 1;
}
g_pCompilerData->intermediateResult = g_pElementizer->GetValue();
return true;
}
else if (g_pElementizer->GetType() == type_con_float)
{
if (g_pCompilerData->intMode == 1)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_inaifpe];
return false;
}
else
{
g_pCompilerData->intMode = 2;
}
g_pCompilerData->intermediateResult = g_pElementizer->GetValue();
return true;
}
else if (g_pElementizer->GetType() == type_float)
{
if (g_pCompilerData->intMode == 1)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_inaifpe];
return false;
}
else
{
g_pCompilerData->intMode = 2;
}
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
g_pCompilerData->intMode = 1;
ResolveExpression(); // integer mode
g_pCompilerData->intMode = 2;
if (!g_pElementizer->GetElement(type_right))
{
return false;
}
int value = g_pCompilerData->mathStack[g_pCompilerData->mathCurrent - 1];
g_pCompilerData->mathCurrent--;
float fValue = (float)(value);
g_pCompilerData->intermediateResult = *(int*)(&fValue);
return true;
}
else if (g_pElementizer->GetType() == type_round)
{
if (g_pCompilerData->intMode == 2)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_fpnaiie];
return false;
}
else
{
g_pCompilerData->intMode = 1;
}
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
g_pCompilerData->intMode = 2;
ResolveExpression(); // float mode
g_pCompilerData->intMode = 1;
if (!g_pElementizer->GetElement(type_right))
{
return false;
}
// convert float to rounded integer
int value = g_pCompilerData->mathStack[g_pCompilerData->mathCurrent - 1];
g_pCompilerData->mathCurrent--;
float fValue = *(float*)(&value);
g_pCompilerData->intermediateResult = (int)(fValue + 0.5f);
return true;
}
else if (g_pElementizer->GetType() == type_trunc)
{
if (g_pCompilerData->intMode == 2)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_fpnaiie];
return false;
}
else
{
g_pCompilerData->intMode = 1;
}
if (!g_pElementizer->GetElement(type_left))
{
return false;
}
g_pCompilerData->intMode = 2;
ResolveExpression(); // float mode
g_pCompilerData->intMode = 1;
if (!g_pElementizer->GetElement(type_right))
{
return false;
}
// convert float to truncated integer
int value = g_pCompilerData->mathStack[g_pCompilerData->mathCurrent - 1];
g_pCompilerData->mathCurrent--;
float fValue = *(float*)(&value);
g_pCompilerData->intermediateResult = (int)(fValue);
return true;
}
if (g_pCompilerData->bOperandMode)
{
bool bLocal = false;
if (!CheckLocal(bLocal))
{
return false;
}
}
bool bUndefined = false;
if (!CheckUndefined(bUndefined))
{
return false;
}
if (bUndefined)
{
if (!g_pCompilerData->bMustResolve)
{
g_pCompilerData->intermediateResult = 0;
}
return true;
}
else if (g_pElementizer->GetType() == type_asm_org)
{
if (g_pCompilerData->bOperandMode)
{
g_pCompilerData->intermediateResult = g_pCompilerData->cog_org >> 2;
return true;
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_oinah];
return false;
}
}
else if (g_pElementizer->GetType() == type_reg)
{
if (g_pCompilerData->bOperandMode)
{
if (g_pCompilerData->intMode == 2)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_fpnaiie];
return false;
}
else
{
g_pCompilerData->intMode = 1;
}
g_pCompilerData->intermediateResult = g_pElementizer->GetValue();
g_pCompilerData->intermediateResult |= 0x1E0;
return true;
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_rinah];
return false;
}
}
else if (g_pElementizer->GetType() == type_obj)
{
if (!g_pElementizer->GetElement(type_pound))
{
return false;
}
char id = (g_pElementizer->GetValue() & 0x0000FF00) >> 8;
if (!GetObjSymbol(type_objcon, id))
{
return false;
}
return CheckConstant(bConstant);
}
else if (g_pElementizer->GetType() == type_at)
{
if (g_pCompilerData->intMode == 2)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_fpnaiie];
return false;
}
else
{
g_pCompilerData->intMode = 1;
}
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (CheckDat())
{
g_pCompilerData->intermediateResult = g_pElementizer->GetValue();
return true;
}
bool bUndefinedCheck = false;
if (!CheckUndefined(bUndefinedCheck))
{
return false;
}
if (bUndefinedCheck)
{
if (!g_pCompilerData->bMustResolve)
{
g_pCompilerData->intermediateResult = 0;
}
bConstant = false;
return true;
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eads];
return false;
}
}
else if (CheckDat())
{
if (g_pCompilerData->intMode == 2)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_fpnaiie];
return false;
}
else
{
g_pCompilerData->intMode = 1;
}
if (g_pCompilerData->bOperandMode)
{
// use org address in value 2
g_pCompilerData->intermediateResult = g_pElementizer->GetValue2();
// check for valid long address
if ((g_pCompilerData->intermediateResult & 0x03) != 0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_ainl];
return false;
}
// convert to long index
g_pCompilerData->intermediateResult >>= 2;
if (g_pCompilerData->intermediateResult >= 0x1F0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_aioor];
return false;
}
}
else
{
g_pCompilerData->intermediateResult = g_pElementizer->GetValue();
}
return true;
}
bConstant = false;
return true;
}
bool PreviewOp()
{
int i = g_pElementizer->GetOpType();
int check = 0x00AACD8F; // 00000000 10101010 11001101 10001111
check >>= i;
if (check & 1)
{
if (g_pCompilerData->intMode == 2)
{
// integer only op while in float mode
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_ionaifpe];
return false;
}
// force integer mode
g_pCompilerData->intMode = 1;
}
return true;
}
bool PerformPush()
{
if (g_pCompilerData->mathCurrent > 9)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eitc];
return false;
}
g_pCompilerData->mathStack[g_pCompilerData->mathCurrent] = g_pCompilerData->intermediateResult;
g_pCompilerData->mathCurrent++;
return true;
}
bool PerformBinary()
{
g_pCompilerData->mathCurrent--;
return PerformOp();
}
bool PerformOp()
{
if (g_pCompilerData->bUndefined)
{
g_pCompilerData->mathStack[g_pCompilerData->mathCurrent-1] = 0;
return true;
}
int value1 = g_pCompilerData->mathStack[g_pCompilerData->mathCurrent - 1];
int value2 = g_pCompilerData->mathStack[g_pCompilerData->mathCurrent];
float fValue1 = *((float*)(&value1));
float fValue2 = *((float*)(&value2));
int result = 0;
float fResult = 0.0f;
switch(g_pCompilerData->savedOp[g_pCompilerData->currentOp-1])
{
case op_ror:
result = ror(value1, (value2 & 0xFF));
break;
case op_rol:
result = rol(value1, (value2 & 0xFF));
break;
case op_shr:
result = (unsigned int)value1 >> (value2 & 0xFF);
break;
case op_shl:
result = value1 << (value2 & 0xFF);
break;
case op_min: // limit minimum
if (g_pCompilerData->intMode == 2)
{
fResult = (fValue1 < fValue2) ? fValue2 : fValue1;
}
else
{
result = (value1 < value2) ? value2 : value1;
}
break;
case op_max: // limit maximum
if (g_pCompilerData->intMode == 2)
{
fResult = (fValue1 > fValue2) ? fValue2 : fValue1;
}
else
{
result = (value1 > value2) ? value2 : value1;
}
break;
case op_neg:
if (g_pCompilerData->intMode == 2)
{
// float neg (using xor)
fResult = -fValue1;
}
else
{
result = -value1;
}
break;
case op_not:
result = ~value1;
break;
case op_and:
result = value1 & value2;
break;
case op_abs:
if (g_pCompilerData->intMode == 2)
{
// float abs
fResult = (float)fabs(fValue1);
}
else
{
result = (value1 < 0) ? -value1 : value1;
}
break;
case op_or:
result = value1 | value2;
break;
case op_xor:
result = value1 ^ value2;
break;
case op_add:
if (g_pCompilerData->intMode == 2)
{
// float add
fResult = fValue1 + fValue2;
}
else
{
result = value1 + value2;
}
break;
case op_sub:
if (g_pCompilerData->intMode == 2)
{
// float sub
fResult = fValue1 - fValue2;
}
else
{
result = value1 - value2;
}
break;
case op_sar:
result = value1 >> (value2 & 0xFF);
break;
case op_rev:
value2 &= 0xFF;
result = 0;
for (int i = 0; i < value2; i++)
{
result <<= 1;
result |= (value1 & 0x01);
value1 >>= 1;
}
break;
case op_log_and:
if (value1 != 0)
{
value1 = 0xFFFFFFFF;
}
if (value2 != 0)
{
value2 = 0xFFFFFFFF;
}
result = value1 & value2;
if (g_pCompilerData->intMode == 2)
{
if (result != 0)
{
fResult = 1.0f;
}
else
{
fResult = 0.0f;
}
}
break;
case op_ncd:
result = 32;
while(!(value1 & 0x80000000) && result > 0)
{
result--;
value1 <<= 1;
}
break;
case op_log_or:
if (value1 != 0)
{
value1 = 0xFFFFFFFF;
}
if (value2 != 0)
{
value2 = 0xFFFFFFFF;
}
result = value1 | value2;
if (g_pCompilerData->intMode == 2)
{
if (result != 0)
{
fResult = 1.0f;
}
else
{
fResult = 0.0f;
}
}
break;
case op_dcd:
result = 1;
result <<= (value1 & 0xFF);
break;
case op_mul:
if (g_pCompilerData->intMode == 2)
{
// float mul
fResult = fValue1 * fValue2;
}
else
{
result = value1 * value2;
}
break;
case op_scl:
{
// calculate the upper 32bits of the 64bit result of multiplying two 32bit numbers
// I did it this way to avoid using compiler specific stuff.
int a = (value1 >> 16) & 0xffff;
int b = value1 & 0xffff;
int c = (value2 >> 16) & 0xffff;
int d = value2 & 0xffff;
int x = a * d + c * b;
int y = (((b * d) >> 16) & 0xffff) + x;
result = (y >> 16) & 0xffff;
result += a * c;
}
break;
case op_div:
if (g_pCompilerData->intMode == 2)
{
// float div
fResult = fValue1 / fValue2;
}
else
{
result = value1 / value2;
}
break;
case op_rem: // remainder (mod)
result = value1 % value2;
break;
case op_sqr: // sqrt
if (g_pCompilerData->intMode == 2)
{
// float sqrt
if (fValue1 < 0.0f)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_ccsronfp];
return false;
}
fResult = (float)sqrt(fValue1);
}
else
{
for (result = 0; value1 >= (2*result)+1; value1 -= (2*result++)+1);
}
break;
case op_cmp_b:
case op_cmp_a:
case op_cmp_ne:
case op_cmp_e:
case op_cmp_be:
case op_cmp_ae:
if (g_pCompilerData->intMode == 2)
{
// float cmp
if (fValue1 < fValue2)
{
result = 1;
}
else if (fValue1 > fValue2)
{
result = 2;
}
else
{
result = 4;
}
result &= g_pCompilerData->savedOp[g_pCompilerData->currentOp-1];
if (result != 0)
{
fResult = 1.0f;
}
else
{
fResult = 0.0f;
}
}
else
{
if (value1 < value2)
{
result = 1;
}
else if (value1 > value2)
{
result = 2;
}
else
{
result = 4;
}
result &= g_pCompilerData->savedOp[g_pCompilerData->currentOp-1];
if (result != 0)
{
result = 0xFFFFFFFF;
}
}
break;
case op_log_not:
result = !value1;
if (g_pCompilerData->intMode == 2)
{
if (result != 0)
{
fResult = 1.0f;
}
else
{
fResult = 0.0f;
}
}
else
{
if (result != 0)
{
result = 0xFFFFFFFF;
}
}
break;
}
if (g_pCompilerData->intMode == 2)
{
result = *(int*)(&fResult);
}
g_pCompilerData->mathStack[g_pCompilerData->mathCurrent - 1] = result;
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,876 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// InstructionBlockCompiler.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
#include "CompileUtilities.h"
//////////////////////////////////////////
// declarations for internal functions
//
bool CompileBlock_IfOrIfNot(int column, int bIf);
bool CompileBlock_Case(int column);
bool CompileBlock_Repeat(int column);
static int s_column = 0;
//////////////////////////////////////////
// exported functions
//
bool CompileTopBlock()
{
g_pCompilerData->bnest_ptr = 0;
g_pCompilerData->bstack_ptr = 0;
StringConstant_PreProcess();
if (!CompileBlock(0))
{
return false;
}
// enter a return into obj
if (!EnterObj(0x32)) // 0x32 = 00110010b
{
return false;
}
return StringConstant_PostProcess();
}
bool CompileBlock(int column)
{
bool bEof = false;
while (!bEof)
{
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_end)
{
continue;
}
if (g_pElementizer->GetType() == type_block)
{
break;
}
s_column = g_pElementizer->GetColumn();
if (s_column <= column)
{
break;
}
if (g_pElementizer->GetType() == type_if)
{
if (!CompileBlock_IfOrIfNot(s_column, true))
{
return false;
}
}
else if (g_pElementizer->GetType() == type_ifnot)
{
if (!CompileBlock_IfOrIfNot(s_column, false))
{
return false;
}
}
else if (g_pElementizer->GetType() == type_case)
{
if (!CompileBlock_Case(s_column))
{
return false;
}
}
else if (g_pElementizer->GetType() == type_repeat)
{
if (!CompileBlock_Repeat(s_column))
{
return false;
}
}
else
{
if (!CompileInstruction())
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
}
}
g_pElementizer->Backup();
return true;
}
//////////////////////////////////////////
// internal function definitions
//
bool CompileIfOrIfNot_FinalJmp(int& addressCount)
{
if (!EnterObj(0x04)) // jmp
{
return false;
}
if (!BlockStack_CompileAddress(0))
{
return false;
}
BlockStack_Write(addressCount, g_pCompilerData->obj_ptr);
addressCount++;
return true;
}
bool CompileIfOrIfNot_Condition(int& addressCount, unsigned char byteCode)
{
if (!CompileExpression())
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
if (!EnterObj(byteCode))
{
return false;
}
if (!BlockStack_CompileAddress(addressCount))
{
return false;
}
return true;
}
bool CompileIfOrIfNot_ElseCondition(int& addressCount, unsigned char byteCode)
{
if (!CompileIfOrIfNot_FinalJmp(addressCount))
{
return false;
}
if (addressCount < (if_limit + 2))
{
return CompileIfOrIfNot_Condition(addressCount, byteCode);
}
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_loxee];
return false;
}
bool CompileIfOrIfNot(int column, int param)
{
int addressCount = 1;
if (!CompileIfOrIfNot_Condition(addressCount, (unsigned char)(param)))
{
return false;
}
bool bEof = false;
while (!bEof)
{
if (!CompileBlock(column))
{
return false;
}
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (bEof)
{
break;
}
s_column = g_pElementizer->GetColumn();
if (s_column < column)
{
g_pElementizer->Backup();
break;
}
if (g_pElementizer->GetType() == type_elseif)
{
if (!CompileIfOrIfNot_ElseCondition(addressCount, 0x0A))
{
return false;
}
}
else if (g_pElementizer->GetType() == type_elseifnot)
{
if (!CompileIfOrIfNot_ElseCondition(addressCount, 0x0B))
{
return false;
}
}
else if (g_pElementizer->GetType() == type_else)
{
if (!CompileIfOrIfNot_FinalJmp(addressCount))
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
if (!CompileBlock(column))
{
return false;
}
break;
}
else
{
g_pElementizer->Backup();
break;
}
}
BlockStack_Write(addressCount, g_pCompilerData->obj_ptr); // set last address
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set final address
return true;
}
bool CompileBlock_IfOrIfNot(int column, int bIf)
{
if (!BlockNest_New(type_if, if_limit+3))
{
return false;
}
if (!OptimizeBlock(column, bIf ? 0x0A : 0x0B, &CompileIfOrIfNot))
{
return false;
}
BlockNest_End();
return true;
}
bool CompileCase(int column, int param)
{
param = param; // stop warning
if (!BlockStack_CompileConstant())
{
return false;
}
if (!CompileExpression())
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
int savedSourcePtr = g_pElementizer->GetSourcePtr();
int otherSourcePtr = 0;
bool bOther = false;
int caseCount = 0;
bool bEof = false;
while (!bEof)
{
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (bEof)
{
break;
}
if (g_pElementizer->GetType() == type_end)
{
continue;
}
s_column = g_pElementizer->GetColumn();
g_pElementizer->Backup();
if (s_column <= column)
{
break;
}
if (bOther) // if we have OTHER: it should have been the last case, so we shouldn't get here again
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_omblc];
return false;
}
if (g_pElementizer->GetType() == type_other)
{
bOther = true;
if (!g_pElementizer->GetNext(bEof)) // get/skip 'other'
{
return false;
}
otherSourcePtr = g_pCompilerData->source_start; // save the pointer to the beginning of 'other'
}
else
{
caseCount++;
if (caseCount > case_limit)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_loxce];
return false;
}
for (;;)
{
bool bRange = false;
if (!CompileRange(bRange))
{
return false;
}
if (!EnterObj(bRange ? 0x0E : 0x0D)) // enter bytecode for case range or case value into obj
{
return false;
}
if (!BlockStack_CompileAddress(caseCount))
{
return false;
}
if (!g_pElementizer->CheckElement(type_comma))
{
break;
}
}
}
if (!g_pElementizer->GetElement(type_colon))
{
return false;
}
if (!SkipBlock(s_column))
{
return false;
}
}
if (caseCount == 0)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_nce];
return false;
}
if (bOther)
{
// set the source pointer to where the OTHER is at, then get it to set the column
g_pElementizer->SetSourcePtr(otherSourcePtr);
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
int new_column = g_pElementizer->GetColumn();
// skip the colon
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (!CompileBlock(new_column))
{
return false;
}
}
if (!EnterObj(0x0C)) // casedone, end of range checks
{
return false;
}
g_pElementizer->SetSourcePtr(savedSourcePtr);
caseCount = 0;
bOther = false;
bEof = false;
while(!bEof)
{
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (bEof)
{
break;
}
if (g_pElementizer->GetType() == type_end)
{
continue;
}
s_column = g_pElementizer->GetColumn();
g_pElementizer->Backup();
if (s_column <= column)
{
break;
}
if (g_pElementizer->GetType() == type_other)
{
// skip over other, already compiled
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (!SkipBlock(s_column))
{
return false;
}
}
else
{
// skip over range/values(s), already compiled
for (;;)
{
if (!SkipRange())
{
return false;
}
if (!g_pElementizer->CheckElement(type_comma))
{
break;
}
}
caseCount++;
BlockStack_Write(caseCount, g_pCompilerData->obj_ptr);
if (!g_pElementizer->GetElement(type_colon))
{
return false;
}
if (!CompileBlock(s_column))
{
return false;
}
if (!EnterObj(0x0C)) // casedone
{
return false;
}
}
}
BlockStack_Write(0, g_pCompilerData->obj_ptr);
return true;
}
bool CompileBlock_Case(int column)
{
if (!BlockNest_New(type_case, case_limit+1))
{
return false;
}
if (!OptimizeBlock(column, 0, &CompileCase))
{
return false;
}
BlockNest_End();
return true;
}
static bool s_bHasPost = false;
bool CompileRepeatPlain(int column, int param)
{
param = param; // stop warning
BlockStack_Write(2, g_pCompilerData->obj_ptr); // set reverse address
if (!s_bHasPost)
{
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set plain 'next' address
}
if (!CompileBlock(column))
{
return false;
}
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
unsigned char byteCode = 0x04;
if (!bEof)
{
s_column = g_pElementizer->GetColumn();
if (s_column < column)
{
g_pElementizer->Backup();
}
else
{
// check for post while or until
int postType = g_pElementizer->GetType();
if ((postType == type_while) ||
(postType == type_until))
{
s_bHasPost = true;
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set post-while/until 'next' address
if (!CompileExpression()) // compile post-while/until expression
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
byteCode = (postType == type_while) ? 0x0B : 0x0A;
}
else
{
g_pElementizer->Backup();
}
}
}
if (!EnterObj(byteCode))
{
return false;
}
if (!BlockStack_CompileAddress(2)) // compile reverse address
{
return false;
}
BlockStack_Write(1, g_pCompilerData->obj_ptr); // set 'quit' address
return true;
}
bool CompileRepeatPreWhileOrUntil(int column, int param)
{
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set 'next'/reverse address
if (!CompileExpression()) // compile pre-while/until expression
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
if (!EnterObj((unsigned char)(param & 0xFF))) // enter the passed in bytecode (jz or jnz)
{
return false;
}
if (!BlockStack_CompileAddress(1)) // compile forward address
{
return false;
}
if (!CompileBlock(column)) // compile repeat-while/until block
{
return false;
}
if (!EnterObj(0x04)) // (jmp)
{
return false;
}
if (!BlockStack_CompileAddress(0)) // compile reverse address
{
return false;
}
BlockStack_Write(1, g_pCompilerData->obj_ptr); // set 'quit'/forward address
return true;
}
bool CompileRepeatCount(int column, int param)
{
param = param; // stop warning
if (!CompileExpression()) // compile count expression
{
return false;
}
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
if (!EnterObj(0x08)) // (tjz)
{
return false;
}
if (!BlockStack_CompileAddress(1)) // compile forward address
{
return false;
}
BlockStack_Write(2, g_pCompilerData->obj_ptr); // set reverse address
if (!CompileBlock(column)) // compile repeat-count block
{
return false;
}
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set 'next' address
if (!EnterObj(0x09)) // (djnz)
{
return false;
}
if (!BlockStack_CompileAddress(2)) // compile reverse address
{
return false;
}
BlockStack_Write(1, g_pCompilerData->obj_ptr); // set 'quit'/forward address
return true;
}
bool CompileRepeatVariable(int column, int param)
{
param = param; // stop warning
unsigned char varType = 0;
unsigned char varSize = 0;
int varAddress = 0;
int varIndexSourcePtr = 0;
if (!GetVariable(varType, varSize, varAddress, varIndexSourcePtr))
{
return false;
}
bool bEof = false;
if (!g_pElementizer->GetNext(bEof)) // get 'from'
{
return false;
}
if (g_pElementizer->GetType() != type_from)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_efrom];
return false;
}
int fromSourcePtr = g_pElementizer->GetSourcePtr();
g_pCompilerData->str_enable = false;
if (!CompileExpression()) // compile 'from' expression (string not allowed)
{
return false;
}
g_pCompilerData->str_enable = true;
if (!CompileVariable(1, 0, varType, varSize, varAddress, varIndexSourcePtr)) // compile var write
{
return false;
}
BlockStack_Write(2, g_pCompilerData->obj_ptr); // set reverse address
if (!g_pElementizer->GetNext(bEof)) // get 'to'
{
return false;
}
if (g_pElementizer->GetType() != type_to)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_eto];
return false;
}
g_pCompilerData->str_enable = false;
if (!SkipExpression()) // skip 'to' expression (string not allowed)
{
return false;
}
g_pCompilerData->str_enable = true;
if (!g_pElementizer->GetNext(bEof)) // check for 'step'
{
return false;
}
unsigned char byteCode = 0;
if (g_pElementizer->GetType() == type_step)
{
// handle step
int savedSourcePtr = g_pElementizer->GetSourcePtr();
g_pCompilerData->str_enable = false;
if (!SkipExpression()) // skip 'step' expression (string not allowed)
{
return false;
}
g_pCompilerData->str_enable = true;
if (!g_pElementizer->GetElement(type_end))
{
return false;
}
if (!CompileBlock(column))
{
return false;
}
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set 'next' address
if (!CompileOutOfSequenceExpression(savedSourcePtr)) // compile the step expression
{
return false;
}
byteCode = 0x06; // (repeat-var w/step)
}
else if (g_pElementizer->GetType() == type_end)
{
// no step, compile block
if (!CompileBlock(column))
{
return false;
}
BlockStack_Write(0, g_pCompilerData->obj_ptr); // set 'next' address
byteCode = 0x02; // (repeat-var)
}
else
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_esoeol];
return false;
}
int savedSourcePtr = g_pElementizer->GetSourcePtr();
g_pElementizer->SetSourcePtr(fromSourcePtr);
if (!CompileExpression()) // compile 'from' expression
{
return false;
}
if (!g_pElementizer->GetNext(bEof)) // skip 'to'
{
return false;
}
if (!CompileExpression()) // compile 'to' expression
{
return false;
}
g_pElementizer->SetSourcePtr(savedSourcePtr);
if (!CompileVariable_Assign(byteCode, varType, varSize, varAddress, varIndexSourcePtr)) // compile repeat-var
{
return false;
}
if (!BlockStack_CompileAddress(2)) // compile reverse address
{
return false;
}
BlockStack_Write(1, g_pCompilerData->obj_ptr); // set 'quit'/forward address
return true;
}
bool CompileBlock_Repeat(int column)
{
if (!BlockNest_New(type_repeat, 3))
{
return false;
}
// determine which type of repeat
bool (*pCompileFunc)(int, int) = 0;
int param = 0;
bool bEof = false;
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
if (g_pElementizer->GetType() == type_end)
{
// repeat
pCompileFunc = &CompileRepeatPlain;
s_bHasPost = false; // assume it doesn't have a post while or until (will be detected)
}
else if (g_pElementizer->GetType() == type_while)
{
// repeat while <exp>
pCompileFunc = &CompileRepeatPreWhileOrUntil;
param = 0x0A;
}
else if (g_pElementizer->GetType() == type_until)
{
// repeat until <exp>
pCompileFunc = &CompileRepeatPreWhileOrUntil;
param = 0x0B;
}
else
{
g_pElementizer->Backup();
int savedSourcePtr = g_pElementizer->GetSourcePtr();
if (!SkipExpression())
{
return false;
}
if (!g_pElementizer->GetNext(bEof))
{
return false;
}
g_pElementizer->SetSourcePtr(savedSourcePtr);
if (g_pElementizer->GetType() == type_end)
{
// repeat <exp>
pCompileFunc = &CompileRepeatCount;
// redo blocknest type
BlockNest_Redo(type_repeat_count);
}
else
{
// repeat var from <exp> to <exp> step <exp>
pCompileFunc = &CompileRepeatVariable;
}
}
if (!OptimizeBlock(column, param, pCompileFunc))
{
return false;
}
BlockNest_End();
return true;
}
bool OptimizeBlock(int column, int param, bool (*pCompileFunction)(int, int))
{
int savedSourcePtr = g_pElementizer->GetSourcePtr();
int savedObjPtr = g_pCompilerData->obj_ptr;
int size = 0;
for (;;)
{
g_pElementizer->SetSourcePtr(savedSourcePtr);
g_pCompilerData->obj_ptr = savedObjPtr;
if (!(*pCompileFunction)(column, param))
{
return false;
}
// (re)compile until same size twice
if (size != g_pCompilerData->obj_ptr)
{
size = g_pCompilerData->obj_ptr;
}
else
{
break;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,73 @@
# cross compilation scheme taken from Eric Smith's spin2cpp compiler
# if CROSS is defined, we are building a cross compiler
# possible targets are: win32, rpi
ifeq ($(CC),)
CC=gcc
endif
ifeq ($(CXX),)
CXX=g++
endif
ifeq ($(CROSS),win32)
CC=i686-w64-mingw32-gcc
CXX=i686-w64-mingw32-g++
EXT=.exe
BUILD=./build-win32
else ifeq ($(CROSS),rpi)
CC=arm-linux-gnueabihf-gcc
CXX=arm-linux-gnueabihf-g++
EXT=
BUILD=./build-rpi
else
EXT=
BUILD=./build
endif
OS:=$(shell uname)
ifeq ($(OS),Darwin)
CFLAGS+=-Wall -g -Wno-self-assign
else
CFLAGS+=-Wall -g $(MSTATIC)
endif
CXXFLAGS += $(CFLAGS)
LIBNAME=$(BUILD)/libopenspin.a
SRCDIR=.
OBJ=$(BUILD)/BlockNestStackRoutines.o \
$(BUILD)/CompileDatBlocks.o \
$(BUILD)/CompileExpression.o \
$(BUILD)/CompileInstruction.o \
$(BUILD)/CompileUtilities.o \
$(BUILD)/DistillObjects.o \
$(BUILD)/Elementizer.o \
$(BUILD)/ErrorStrings.o \
$(BUILD)/ExpressionResolver.o \
$(BUILD)/InstructionBlockCompiler.o \
$(BUILD)/StringConstantRoutines.o \
$(BUILD)/SymbolEngine.o \
$(BUILD)/Utilities.o \
$(BUILD)/UnusedMethodUtils.o \
$(BUILD)/PropellerCompiler.o \
$(BUILD)/CompileSpin.o \
$(BUILD)/flexbuf.o \
$(BUILD)/preprocess.o \
$(BUILD)/textconvert.o \
$(BUILD)/objectheap.o
all: $(BUILD) $(LIBNAME) Makefile
$(LIBNAME): $(OBJ)
$(AR) rs $@ $^
$(BUILD)/%.o: $(SRCDIR)/%.cpp
$(CXX) $(CXXFLAGS) -o $@ -c $<
$(BUILD):
mkdir -p $(BUILD)
clean:
rm -rf $(BUILD)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,193 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// PropellerCompiler.h
//
#ifndef _PROPELLER_COMPILER_H_
#define _PROPELLER_COMPILER_H_
#include "UnusedMethodUtils.h"
//
// OpenSpin code uses _stricmp() which is the VC++ name for the function.
// this needs to be remapped to stricmp or strcasecmp depending on the compiler and OS being compiled on
// GCC prior to version 4.8 have strcasecmp on both linux and windows
// GCC 4.8 and newer on linux appears to still have strcasecmp, but GCC 4.8 and newer on windows does not (it has stricmp instead)
//
#if defined(__linux__)
// we are on linux, then always use strcasecmp
#define _stricmp strcasecmp
#else
#if __GNUC__
// if GCC version is 4.8 or greater use stricmp, else use strcasecmp
#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 8 ))
#define _stricmp stricmp
#else
#define _stricmp strcasecmp
#endif
#endif
#endif
//
// no longer compatible with Prop Tool / Propellent
//
#define language_version '0'
#define loc_limit 0x8000
#define var_limit 0x8000
#define min_obj_limit 0x00020000
#define file_limit 32
#define data_limit 0x20000
#define info_limit 1000
#define distiller_limit 0x4000
#define symbol_limit 256 // was 32
#define pubcon_list_limit 0x8000
#define block_nest_limit 8
#define block_stack_limit 4096
#define case_limit 256
#define if_limit 16
#define str_limit 256
#define str_buffer_limit 0x8000
enum infoType
{
info_con = 0, // data0 = value (must be followed by info_con_float)
info_con_float, // data0 = value
info_dat, // data0/1 = obj start/finish
info_dat_symbol, // data0 = value, data2 = offset, data1 = size
info_pub, // data0/1 = obj start/finish, data2/3 = name start/finish
info_pri, // data0/1 = obj start/finish, data2/3 = name start/finish
info_pub_param, // data0 = pub index, data3 = param index, data2/3 = pub name start/finish
info_pri_param // data0 = pri index, data3 = param index, data2/3 = pri name start/finish
};
// Propeller Compiler Interface Structure
struct CompilerData
{
bool error; // Compilation status; error if true, success if false
const char* error_msg; // Pointer to error string
int compile_mode; // Compile Mode; 0 = normal compile, 1 = Propeller Development compile
char* source; // Pointer to source data
int source_start; // Offending item start (if error)
int source_finish; // Offending item end (+1) (if error)
char* list; // Pointer to list data
unsigned int list_limit; // Max size of list data
unsigned int list_length; // Length of list data
char* doc; // Pointer to document data
unsigned int doc_limit; // Max size of document data
unsigned int doc_length; // Length of document data
unsigned char* obj; // Object binary for currently being compiled obj
int obj_ptr; // Length of Object binary
int obj_limit; // size of buffer allocated for obj
int obj_files; // Number of object files referenced by source
char obj_filenames[file_limit*256]; // Object filenames
int obj_name_start[file_limit]; // Starting char of each filename
int obj_name_finish[file_limit]; // Ending character (+1) of each filename
int obj_offsets[file_limit]; // Offsets of final objects in ObjData
int obj_lengths[file_limit]; // Lengths of final objects in ObjData
unsigned char obj_data[data_limit]; // Final top-level object binary
int obj_instances[file_limit]; // Instances per filename
char obj_title[256]; // Object Filename (without path)
int dat_files; // Number of DAT files referenced by source
char dat_filenames[file_limit*256]; // DAT filenames
int dat_name_start[file_limit]; // Starting char of each filename
int dat_name_finish[file_limit]; // Ending character (+1) of each filename
int dat_offsets[file_limit]; // Offsets of final objects in DatData
int dat_lengths[file_limit]; // Lengths of final objects in DatData
unsigned char dat_data[data_limit]; // Binary data
int pre_files; // Number of Precompile files referenced by source
char pre_filenames[file_limit*256]; // Precompile filenames
int pre_name_start[file_limit]; // Starting char of each filename
int pre_name_finish[file_limit]; // Ending character (+1) of each filename
int arc_files; // Number of Archive files referenced by source
char arc_filenames[file_limit*256]; // Archive filenames
int arc_name_start[file_limit]; // Starting char of each filename
int arc_name_finish[file_limit]; // Ending character (+1) of each filename
int info_count; // Number of information records for object
int info_start[info_limit]; // Start of source related to this info
int info_finish[info_limit]; // End (+1) of source related to this info
int info_type[info_limit]; // 0 = CON, 1 CON(float), 2 = DAT, 3 = DAT Symbol, 4 = PUB, 5 = PRI, 6 = PUB_PARAM, 7 = PRI_PARAM
int info_data0[info_limit]; // Info field 0: if CON = Value, if DAT/PUB/PRI = Start addr in object, if DAT Symbol = value, if PARAM = pub/pri index
int info_data1[info_limit]; // Info field 1: if DAT/PUB/PRI = End+1 addr in object, if DAT Symbol = size, if PARAM = param index
int info_data2[info_limit]; // Info field 2: if PUB/PRI/PARAM = Start of pub/pri name in source, if DAT Symbol = offset (in cog)
int info_data3[info_limit]; // Info field 3: if PUB/PRI/PARAM = End+1 of pub/pri name in source
int info_data4[info_limit]; // Info field 4: if PUB/PRI = index|param count
int distilled_longs; // Total longs optimized out of object
unsigned char first_pub_parameters;
int stack_requirement; // Stack requirement for top-level object
unsigned char clkmode;
int clkfreq;
int debugbaud; // 0 = no debug, > 0 = debug at DebugBaud rate
bool bDATonly; // only compile DAT sections (into obj)
// only add new stuff below here
bool bBinary; // true for binary, false for eeprom
unsigned int eeprom_size; // size of eeprom
unsigned int vsize; // used to hold last vsize (in case it is greater than 65536)
unsigned int psize; // used to hold last psize (in case it is greater than 65536)
char current_filename[256]; // name of object being compiled at the moment
bool bUnusedMethodElimination; // true if unused method elimination is on
bool bFinalCompile; // set to true after unused method scan complete
int unused_obj_files; // number of unused object files
char obj_unused[file_limit*256]; // hold filenames of unused objects
int unused_methods; // number of unused methods
char method_unused[32*file_limit*symbol_limit]; // hold names of unused methods
char* current_file_path; // full path of the current file being compiled (points to entries in s_filesAccessed[])
};
// public functions
extern CompilerData* InitStruct();
extern void Cleanup();
extern const char* Compile1();
extern const char* Compile2();
extern bool GetErrorInfo(int& lineNumber, int& column, int& offsetToStartOfLine, int& offsetToEndOfLine, int& offendingItemStart, int& offendingItemEnd);
#endif // _PROPELLER_COMPILER_H_
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,134 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// PropellerCompilerInternal.h
//
#ifndef _PROPELLER_COMPILER_INTERNAL_H_
#define _PROPELLER_COMPILER_INTERNAL_H_
#include "PropellerCompiler.h"
struct CompilerDataInternal : public CompilerData
{
// this stuff is misc globals from around the asm code
int var_byte;
int var_word;
int var_long;
int var_ptr;
int obj_start;
int obj_count;
int asm_local;
unsigned char pubcon_list[pubcon_list_limit];
int pubcon_list_size;
char symbolBackup[symbol_limit+2]; // used when entering a symbol into the symbol table
bool doc_flag;
bool doc_mode;
int cog_org;
int print_length;
// these are used by EnterInfo() to fill in info_* stuff above
// various code fills these in and then calls EnterInfo()
// I kept it this way because at the point the code calls EnterInfo() it doesn't
// always have the values available to just pass as parameters.
int inf_start; // Start of source related to this info
int inf_finish; // End (+1) of source related to this info
int inf_type; // 0 = CON, 1 CON(float), 2 = DAT, 3 = DAT Symbol, 4 = PUB, 5 = PRI, 6 = PUB_PARAM, 7 = PRI_PARAM
int inf_data0; // Info field 0: if CON = Value, if DAT/PUB/PRI = Start addr in object, if DAT Symbol = value, if PARAM = pub/pri index
int inf_data1; // Info field 1: if DAT/PUB/PRI = End+1 addr in object, if DAT Symbol = size, if PARAM = param index
int inf_data2; // Info field 2: if PUB/PRI/PARAM = Start of pub/pri name in source, if DAT Symbol = offset (in cog)
int inf_data3; // Info field 3: if PUB/PRI/PARAM = End+1 of pub/pri name in source
int inf_data4; // Info field 4: if PUB/PRI = index|param count
// used by GetFileName/AddFileName
char filename[255];
// these are used by the CompileConBlocks() code
int enum_valid;
int enum_value;
int assign_flag;
int assign_type;
int assign_value;
// used by CompileDatBlocks code
int orgx;
// used by ResolveExpression code
int intMode; // 0 = uncommitted, 1 = int mode, 2 = float mode
int precedence; // current precedence
bool bMustResolve; // the expression must resolve
bool bUndefined; // the expression is undefined
bool bOperandMode; // when dealing with a PASM operand
int mathCurrent; // index into mathStack[]
int mathStack[16]; // holds the intermediate values during expression resolving
int intermediateResult; // the current intermediate result
int currentOp; // index into savedOp[]
int savedOp[32]; // stack of operations to perform during expression resolving
// used by Object Distiller (DistillObjects.cpp)
int dis_ptr;
unsigned short dis[distiller_limit];
// used for string constant processing (StringConstantRoutines.cpp)
bool str_enable;
bool str_patch_enable;
int str_count;
int str_buffer_ptr;
unsigned char str_buffer[str_buffer_limit];
int str_source[str_limit];
int str_patch[str_limit];
int str_offset[str_limit];
int str_index;
// used by InstructionBlockCompiler.cpp & BlockNestStackRoutines.cpp
int bnest_ptr;
unsigned char bnest_type[block_nest_limit];
int bstack_ptr;
int bstack_base[block_nest_limit];
int bstack[block_stack_limit];
};
class Elementizer;
class SymbolEngine;
// shared globals
extern Elementizer* g_pElementizer;
extern CompilerDataInternal* g_pCompilerData;
extern SymbolEngine* g_pSymbolEngine;
#endif // _PROPELLER_COMPILER_INTERNAL_H_
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,136 @@
//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// StringConstantRoutines.cpp
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Utilities.h"
#include "PropellerCompilerInternal.h"
#include "SymbolEngine.h"
#include "Elementizer.h"
#include "ErrorStrings.h"
void StringConstant_PreProcess()
{
g_pCompilerData->str_enable = true;
g_pCompilerData->str_patch_enable = true;
g_pCompilerData->str_count = 0;
g_pCompilerData->str_buffer_ptr = 0;
}
bool StringConstant_GetIndex()
{
int strIndex = 0;
for (strIndex = 0; strIndex < g_pCompilerData->str_count; strIndex++)
{
if (g_pCompilerData->str_source[strIndex] == g_pCompilerData->source_start)
{
break;
}
}
if (strIndex == g_pCompilerData->str_count)
{
// new string constant
if (g_pCompilerData->str_count > str_limit)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_tmsc];
return false;
}
g_pCompilerData->str_count++;
g_pCompilerData->str_source[strIndex] = g_pCompilerData->source_start;
g_pCompilerData->str_offset[strIndex] = g_pCompilerData->str_buffer_ptr;
}
else
{
// old
g_pCompilerData->str_buffer_ptr = g_pCompilerData->str_offset[strIndex];
}
g_pCompilerData->str_index = strIndex;
return true;
}
bool StringConstant_EnterChar(unsigned char theChar)
{
if (g_pCompilerData->str_buffer_ptr >= str_buffer_limit)
{
g_pCompilerData->error = true;
g_pCompilerData->error_msg = g_pErrorStrings[error_tmscc];
return false;
}
g_pCompilerData->str_buffer[g_pCompilerData->str_buffer_ptr++] = theChar;
return true;
}
void StringConstant_EnterPatch()
{
if (g_pCompilerData->str_patch_enable)
{
g_pCompilerData->str_patch[g_pCompilerData->str_index] = g_pCompilerData->obj_ptr;
}
}
bool StringConstant_PostProcess()
{
if (g_pCompilerData->str_count > 0)
{
// patch string addresses
int strIndex = 0;
while(g_pCompilerData->str_count > 0)
{
int temp = g_pCompilerData->obj_ptr;
temp += g_pCompilerData->str_offset[strIndex];
temp |= 0x8000;
//short strAddress = ((temp & 0xFF00) >> 8) | ((temp & 0x00FF) << 8); // xchg ah,al
//*((short*)&(g_pCompilerData->obj[g_pCompilerData->str_patch[strIndex]])) = strAddress;
g_pCompilerData->obj[g_pCompilerData->str_patch[strIndex]] = (unsigned char)((temp >> 8) & 0xFF);
g_pCompilerData->obj[g_pCompilerData->str_patch[strIndex] + 1] = (unsigned char)(temp & 0xFF);
strIndex++;
g_pCompilerData->str_count--;
}
// enter strings into obj
for (int i = 0; i < g_pCompilerData->str_buffer_ptr; i++)
{
if (!EnterObj(g_pCompilerData->str_buffer[i]))
{
return false;
}
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////

Some files were not shown because too many files have changed in this diff Show More