Skip to content

Commit

Permalink
Merge pull request #11 from markdryan/markdryan/v8
Browse files Browse the repository at this point in the history
Specasm v8
  • Loading branch information
markdryan authored Mar 10, 2024
2 parents 99590f3 + 5859ca0 commit f6bbb12
Show file tree
Hide file tree
Showing 64 changed files with 2,005 additions and 460 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ COMMON =\
line_common.c

SRCS =\
clipboard.c \
editor.c \
editor_tests.c \
editor_test_content.c \
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,4 @@ make -j
make tests
```

This will create a folder called tests in the unit folder. Inside this folder are 3 files that need to be copied to the same directory on your spectrum. The unitnext file is a dotn file which needs to be copied to the /dot directory. Then make sure you are in the folder containing the test_bad and test_op files and type .unitnext.
This will create a folder called tests in the unit folder. Inside this folder are 3 files that need to be copied to the same directory on your Next. Enter the folder and then type ../unitnext from the command line to launch the tests.
1 change: 1 addition & 0 deletions build/48/specasm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ SPECASM = \
line_parse.o \
line_parse_common.o \
error.o \
specasm_mainloop.o \
state_base.o \
state_dump.o \
state_parse.o \
Expand Down
1 change: 1 addition & 0 deletions build/next/specasm/Make.include
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ peer_file_next.o: peer_file_next.c error.h peer_file.h
util_print_next.o: util_print_next.c util_print_zx.h
samake.o: samake.c peer.h error.h peer_zx.h line.h \
salink.h peer_file.h state_base.h strings.h
clipboard_banked.o: clipboard.c clipboard.h line.h error.h

%.o: %.c
zcc $(CFLAGS) -o $@ -c $<
5 changes: 5 additions & 0 deletions build/next/specasm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ line_dump_banked.o: line_dump.c
ld_parse_banked.o: ld_parse.c
$(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $<

clipboard_banked.o: clipboard.c
$(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_46_H --constsegBANK_46_H --datasegBANK_46_H -c $<

expression_banked.o: expression.c
$(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_43_H --constsegBANK_43_H --datasegBANK_43_H -c $<

Expand All @@ -30,12 +33,14 @@ include Make.include
SPECASM = \
specasm_next.o \
specasm_trampolines.o \
clipboard_banked.o \
line_common.o \
ld_parse_banked.o \
line_dump_banked.o \
line_dump_common.o \
line_parse_banked.o \
line_parse_common.o \
specasm_mainloop.o \
error.o \
state_base.o \
state_dump.o \
Expand Down
1 change: 1 addition & 0 deletions build/next/unit/Make.include
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ peer_file_next.o: peer_file_next.c error.h peer_file.h
util_print_next.o: util_print_next.c util_print_zx.h
samake.o: samake.c peer.h error.h peer_zx.h line.h \
salink.h peer_file.h state_base.h strings.h
clipboard_banked.o: clipboard.c clipboard.h line.h error.h

unittests_zx.o: ../src/unittests_zx.c ../src/error.h ../src/line.h \
../src/state.h ../src/state_base.h ../src/strings.h ../src/test_content.h ../src/test_content_zx.h \
Expand Down
4 changes: 4 additions & 0 deletions build/next/unit/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ line_dump_banked.o: line_dump.c
ld_parse_banked.o: ld_parse.c
$(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_45_H --constsegBANK_45_H --datasegBANK_45_H -c $<

clipboard_banked.o: clipboard.c
$(CC) $(CFLAGS) -DSPECASM_NEXT_BANKED -o $@ --codesegBANK_46_H --constsegBANK_46_H --datasegBANK_46_H -c $<

include Make.include

UNITNEXT = \
error.o \
state_base.o \
specasm_trampolines.o \
line_common.o \
clipboard_banked.o \
ld_parse_banked.o \
line_dump_banked.o \
line_dump_common.o \
Expand Down
91 changes: 84 additions & 7 deletions docs/specasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ Specasm is a Z80 assembler designed to run on the 48k and 128k ZX Spectrums with

The editor can be launched from the command line using the .specasm command. Specasm is an integrated editor/assembler.

> [!TIP]
> The ZX Spectrum Next version of Specasm allows the name of a single .x file to be passed on the command line. This file will be automatically loaded into the editor on startup, e.g., .specasm game.x.
The editor allows one single source, .x, file to be edited at any one time. Each such file can contain a maximum of 512 lines. Each line can be

- An empty line
Expand Down Expand Up @@ -60,11 +63,19 @@ On entering command mode, a '>' prompt appears at the bottom of the screen. Fro
| g *line number* | Moves the cursor to the specified line number|
| f *string* | Searches for the first instance of *string* starting from the current line. There's no wrap around. |

The Next version of Specasm uses one of the Next's 8Kb memory banks to implement a clipboard. This provides a more traditional copy, cut and paste mechanism than the copy and move commands described above, and allows code to be copied from one file to another. Clipboard support is provided via three new Next specific commands.

| Command | Description |
|---------|-------------|
| x | Replaces the contents of the clipboard with the selected code and deletes the code from the current file |
| cc | Replaces the contents of the clipboard with the selected code |
| v | Pastes the contents of the clipboard into the selected code at the cursor position |

#### Selecting Mode

The *sel* command switches the editor into selection mode. In selection mode the user can press the up and down keys to select a block of code. They can also press the 'a' key to select the entire file. Only whole lines can be selected. To cancel the selection and return to editor mode, press SPACE. To delete the selected lines and return to editor mode, press DELETE. To confirm the selection and return to editor mode, press ENTER. Once the selection has been confirmed an '*' will appear in the status row at the bottom of the screen to the right of 'INS' or 'OVR'. This indicates that some lines are currently selected. These lines can be manipulated using the 'd', 'm', 'c' and 'b' commands described above. For example, to count the number of machine code bytes in the selected line, type SYMSHIFT+w followed by 'b' and ENTER. The editor is capable of computing the exact size in bytes of the machine code associated with the instructions and data in the selected region as it has already assembled these instructions and knows exactly how much space they will consume.
The *sel* command switches the editor into selection mode. In selection mode the user can press the up and down keys to select a block of code. They can also press the 'a' key to select the entire file. Only whole lines can be selected. To cancel the selection and return to editor mode, press SPACE. To delete the selected lines and return to editor mode, press DELETE. On the Next, the selected lines may be cut to the clipboard by typing 'x'. To confirm the selection and return to editor mode, press ENTER. Once the selection has been confirmed an '*' will appear in the status row at the bottom of the screen to the right of 'INS' or 'OVR'. This indicates that some lines are currently selected. These lines can be manipulated using the 'd', 'm', 'c','b', 'x' and 'cc' commands described above. For example, to count the number of machine code bytes in the selected line, type SYMSHIFT+w followed by 'b' and ENTER. The editor is capable of computing the exact size in bytes of the machine code associated with the instructions and data in the selected region as it has already assembled these instructions and knows exactly how much space they will consume.

All of the four commands that manipulate selections, cancel the selection once they have finished executing. So if you select a block of text, and then issue the 'b' command to count the number of bytes it consumes, you'll need to reselect the text once more to copy it. In addition, the current selection is cancelled if you make any changes to the contents of the editor, e.g., edit an existing line or insert a new one.
All of the six commands that manipulate selections, cancel the selection once they have finished executing. So if you select a block of text, and then issue the 'b' command to count the number of bytes it consumes, you'll need to reselect the text once more to copy it. In addition, the current selection is cancelled if you make any changes to the contents of the editor, e.g., edit an existing line or insert a new one.

#### The Status Row

Expand Down Expand Up @@ -578,15 +589,23 @@ The reverse process can be performed using the .saexport command. Note the use

## Program structure

A Specasm program is comprised of one or more .x files that occupy the same directory. When you build a Specasm program with the .salink command it looks for all the .x files in the folder in which it is run, and links them all together, concatenating them all into one single file and resolving any addresses, e.g., jump targets. One of the .x files in the current folder must contain a label called **Main**, .e.g, it must have the following statement somewhere within one of the files
A Specasm program is comprised of one or more .x files. When you build a Specasm program with the .salink command it looks for all the .x files in the folder in which it is run, and links them all together, concatenating them all into one single file and resolving any addresses, e.g., jump targets.

> [!TIP]
> Specasm programs can actually span multiple directories. See the +, - and ! directives below.

One of the .x files must contain a label called **Main**, .e.g, it must have the following statement somewhere within one of the files

```
.Main
```

The name of the resulting binary will be derived from the name of the .x file that contains the **Main** label. So if a project places the Main label in a file called **game.x**, the name of the resulting binary created by salink will be **game**.

The salink command will place the code from the .x file that declares the Main label first in the newly created binary. The order in which the rest of the code is written to the binary is arbitrary and the user has no control over this. They can however, ask the linker to generate a map file to figure out where all the symbols ended up. This is done by specifying the **map** directive on one line of one .x file, e.g.,
The salink command will place the code from the .x file that declares the Main label first in the newly created binary. In Specasm versions v7 and eearlier, the order in which the rest of the code is written to the binary is arbitrary and the user has no control over this. In Specasm v8 and above, the order in which the code from the remaining .x files is written to the target binary is defined by the alphabetical order (ascending) of their names. Suppose our program consisted of 3 files, main.x, 02_code.x, 01_data.x, the data/code in main.x would be written first, followed by the contents of 01_data.x, and finally, by the contents of 02_code.x.

You can ask the linker to generate a map file to figure out where all the symbols ended up. This is done by specifying the **map** directive on one line of one .x file, e.g.,

```
map
Expand Down Expand Up @@ -621,9 +640,12 @@ org 23760

will cause the linked program to be assembled at 23760.

The linker doesn't currently create a loader program or a tap file. This can however be done using the samake command.
The linker only creates pure binary files. It isn't capable of creating a loader program or a tap file. This is the task of the samake program, introduced in Specasm v7. See below for more details.

### Libraries
Specasm projects are not limited to the .x files in the main directory. The '-' and '+' directives can be used to include .x files in other directories, allowing a Specasm project to span multiple directories. See below for more details.


### Multi-directory Projects and Libraries

Specasm supports two directives that allow the user to include .x files from other directories in the final executable.

Expand All @@ -634,15 +656,70 @@ Specasm supports two directives that allow the user to include .x files from oth

These are both linker directives rather than assembler directives. The target of these directives are not included directly into the current source file. Instead they are added to the final binary when it is linked. The '-' directive is intended to be used to create custom libraries or to split your program into multiple folders. The '+' directive is intended to be used to include .x files from a future Specasm standard library. The trailing '.x' extension in <filename> is optional.

Here are some examples of their use
If the path passed to a '-' or a '+' directive is itself a directory, Specasm (v8 and above) will add all the .x files it can find in that directory, and only that directory, to the final binary.

Here are some examples of '-' and '+'

```
; include /specasm/gr/rows.x
+gr/rows
; include ./lib/math.x
-lib/math.x
; include all .x files in mylib
; Specasm v8 and above.
-mylib
```

The '-' and '+' directives will not add .x files located in any sub-directory of an included directory. For example,

```
-mylib
```

will add mylib/peqnp.x but it will not add mylib/tests/binpack.x. If mylib/tests/binpack.x is needed in the project it will need to be included separately, either with a

```
-mylib/tests/binpack.x
```

or

```
-mylib/tests
```

if all the .x files in mylib/tests are to be part of the project.

### Binary files

Specasm supports one directive that allows a file to be inserted directly into the final binary at the position at which the directive appears. This is useful when including binary data directly into your final executable rather than shipping it as a separate file and reading it in at runtime, or encoding it using db or dw statements, which isn't really practical when there's a lot of data.

| Directive | Description |
|--------------|--------------------------------------------------------------------------------------------|
| ! <filename> | Filename is either an absolute or a relative path (relative to the current .x file) |

For example,

```
; load pointer to sprite data into hl
ld hl, sprites
; Load size of sprite file into bc
ld bc, =sprite_end-sprites
; do something with the sprites
ret
.sprites
! spritefile
.sprite_end
```

The data in spritefile will be inserted between the .sprites and .sprite_end label in the final binary by the linker. The register bc will contain the size of that file.

## Creating Loaders with SAMAKE

Expand Down
114 changes: 114 additions & 0 deletions src/clipboard.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright contributors to Specasm
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifdef SPECASM_TARGET_NEXT_OPCODES

#include <stdlib.h>
#include <string.h>

#include "clipboard.h"
#include "line.h"

#define SPECASM_CLIP_MAX_SIZE (15 * 512)

static uint8_t clip_buffer[SPECASM_CLIP_MAX_SIZE];

static uint16_t clip_end_ptr;
static uint16_t clip_lines;

#ifdef SPECASM_NEXT_BANKED
void specasm_clip_reset_banked(void)
#else
void specasm_clip_reset(void)
#endif
{
clip_end_ptr = 0;
clip_lines = 0;
}

#ifdef SPECASM_NEXT_BANKED
void specasm_clip_add_line_banked_e(const char *line)
#else
void specasm_clip_add_line_e(const char *line)
#endif
{
uint8_t len;
const char *end_ptr;

/*
* Skip leading and trailing white space. We don't need to store this.
*/

end_ptr = &line[SPECASM_LINE_MAX_LEN - 1];
while ((end_ptr >= line) && (*end_ptr == ' '))
end_ptr--;
end_ptr++;

if (line == end_ptr) {
clip_buffer[clip_end_ptr++] = 0;
} else {
while (*line == ' ')
line++;

len = end_ptr - line;
if (len + clip_end_ptr >= SPECASM_CLIP_MAX_SIZE) {
err_type = SPECASM_ERROR_TOO_MANY_LINES;
return;
}

clip_buffer[clip_end_ptr++] = len;
memcpy(&clip_buffer[clip_end_ptr], line, len);
clip_end_ptr += len;
}
clip_lines++;
}

#ifdef SPECASM_NEXT_BANKED
uint16_t specasm_clip_get_line_banked(uint16_t ptr, char *buffer)
#else
uint16_t specasm_clip_get_line(uint16_t ptr, char *buffer)
#endif
{
uint8_t len;
uint8_t spaces;

if (ptr >= clip_end_ptr)
return 0;

len = clip_buffer[ptr++];
if (len > 0) {
memcpy(buffer, &clip_buffer[ptr], len);
ptr += len;
}

spaces = SPECASM_LINE_MAX_LEN - len;
if (spaces > 0)
memset(&buffer[len], ' ', spaces);
buffer[len + spaces] = 0;

return ptr;
}

#ifdef SPECASM_NEXT_BANKED
uint16_t specasm_clip_get_line_count_banked(void)
#else
uint16_t specasm_clip_get_line_count(void)
#endif
{
return clip_lines;
}

#endif
27 changes: 27 additions & 0 deletions src/clipboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright contributors to Specasm
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef CLIPBOARD_H
#define CLIPBOARD_H

#include <stdint.h>

void specasm_clip_reset(void);
void specasm_clip_add_line_e(const char *line);
uint16_t specasm_clip_get_line(uint16_t ptr, char *buffer);
uint16_t specasm_clip_get_line_count(void);

#endif
Loading

0 comments on commit f6bbb12

Please sign in to comment.