diff --git a/CODE.md b/CODE.md deleted file mode 100644 index 83b06d9..0000000 --- a/CODE.md +++ /dev/null @@ -1,117 +0,0 @@ -## Code conventions - -The code in this project follow certain project-wide conventions, which are -described here. Kernel code and userspace code follow additional conventions -which are described in `kernel/README.md` and `apps/README.md`. - -## Defines - -Each unit can have its own constants, but some constant are made to be defined -externally. We already have some of those external definitions in platform -includes, but we can have more defines than this. - -Many units have a "DEFINES" section listing the constant it expects to be -defined. Make sure that you have these constants defined before you include the -file. - -## Variable management - -Each unit can define variables. These variables are defined as addresses in -RAM. We know where RAM start from the `RAMSTART` constant in platform includes, -but because those parts are made to be glued together in no pre-defined order, -we need a system to align variables from different modules in RAM. - -This is why each unit that has variable expect a `_RAMSTART` -constant to be defined and, in turn, defines a `_RAMEND` constant to -carry to the following unit. - -Thus, code that glue parts together could look like: - - MOD1_RAMSTART .equ RAMSTART - #include "mod1.asm" - MOD2_RAMSTART .equ MOD1_RAMEND - #include "mod2.asm" - -## Register protection - -As a general rule, all routines systematically protect registers they use, -including input parameters. This allows us to stop worrying, each time we call -a routine, whether our registers are all messed up. - -Some routines stray from that rule, but the fact that they destroy a particular -register is documented. An undocumented register change is considered a bug. -Clean up after yourself, you nasty routine! - -Another exception to this rule are "top-level" routines, that is, routines that -aren't designed to be called from other parts of Collapse OS. Those are -generally routines close to an application's main loop. - -It is important to note, however, that shadow registers aren't preserved. -Therefore, shadow registers should only be used in code that doesn't call -routines or that call a routine that explicitly states that it preserves -shadow registers. - -Another important note is that routines returning success with Z generally don't -preserve AF: too complicated. But otherwise, AF is often preserved. For example, -register fiddling routines in core try to preserve AF. - -## Z for success - -The vast majority of routines use the Z flag to indicate success. When Z is set, -it indicates success. When Z is unset, it indicates error. This follows the -tradition of a zero indicating success and a nonzero indicating error. - -Important note: only Z indicate success. Many routines return a meaningful -nonzero value in A and still set Z to indicate success. - -In error conditions, however, most of the time A is set to an error code. - -In many routines, this is specified verbosely, but it's repeated so often that -I started writing it in short form, "Z for success", which means what is -described here. - -## Stack management - -Keeping the stack "balanced" is a big challenge when writing assembler code. -Those push and pop need to correspond, otherwise we end up with completely -broken code. - -The usual "push/pop" at the beginning and end of a routine is rather easy to -manage, nothing special about them. - -The problem is for the "inner" push and pop, which are often necessary in -routines handling more data at once. In those cases, we walk on eggshells. - -A naive approach could be to indent the code between those push/pop, but indent -level would quickly become too big to fit in 80 chars. - -I've tried ASCII art in some places, where comments next to push/pop have "|" -indicating the scope of the push/pop. It's nice, but it makes code complicated -to edit, especially when dense comments are involved. The pipes have to go -through them. - -Of course, one could add descriptions next to each push/pop describing what is -being pushed, and I do it in some places, but it doesn't help much in easily -tracking down stack levels. - -So, what I've started doing is to accompany each "non-routine" (at the -beginning and end of a routine) push/pop with "--> lvl X" and "<-- lvl X" -comments. Example: - - push af ; --> lvl 1 - inc a - push af ; --> lvl 2 - inc a - pop af ; <-- lvl 2 - pop af ; <-- lvl 1 - -I think that this should do the trick, so I'll do this consistently from now on. - -## String length - -Pretty much every routine expecting a string have no provision for a string -that doesn't have null termination within 0xff bytes. Treat strings of such -lengths with extra precaution and distrust proper handling of existing routines -for those strings. - -[zasm]: ../apps/zasm/README.md diff --git a/apps/README.md b/apps/README.md deleted file mode 100644 index 6be1999..0000000 --- a/apps/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# User applications - -This folder contains code designed to be "userspace" application. Unlike the -kernel, which always stay in memory. Those apps here will more likely be loaded -in RAM from storage, ran, then discarded so that another userspace program can -be run. - -That doesn't mean that you can't include that code in your kernel though, but -you will typically not want to do that. - -## Userspace convention - -We execute a userspace application by calling the address it's loaded into. - -This means that userspace applications must be assembled with a proper `.org`, -otherwise labels in its code will be wrong. - -The `.org`, it is not specified by glue code of the apps themselves. It is -expected to be set either in the `user.h` file to through `zasm` 3rd argument. - -That a userspace is called also means that an application, when finished -running, is expected to return with a regular `ret` and a clean stack. - -Whatever calls the userspace app (usually, it will be the shell), should set -HL to a pointer to unparsed arguments in string form, null terminated. - -The userspace application is expected to set A on return. 0 means success, -non-zero means error. - -A userspace application can expect the `SP` pointer to be properly set. If it -moves it, it should take care of returning it where it was before returning -because otherwise, it will break the kernel. - -## Memory management - -Apps in Collapse OS are design to be ROM-compatible, that is, they don't write -to addresses that are part of the code's address space. - -By default, apps set their RAM to begin at the end of the binary because in -most cases, these apps will be ran from RAM. If they're ran from ROM, make sure -to set `USER_RAMSTART` properly in your `user.h` to ensure that the RAM is -placed properly. - -Applications that are ran as a shell (the "shell" app, of course, but also, -possibly, "basic" and others to come) need a manual override to their main -`RAMSTART` constant: You don't want them to run in the same RAM region as your -other userspace apps because if you do, as soon as you launch an app with your -shell, its memory is going to be overwritten! - -What you'll do then is that you'll reserve some space in your memory layout for -the shell and add a special constant in your `user.h`, which will override the -basic one (remember, in zasm, the first `.equ` for a given constant takes -precedence). - -For example, if you want a "basic" shell and that you reserve space right -after your kernel RAM for it, then your `user.h` would contain -`.equ BAS_RAMSTART KERNEL_RAMEND`. - -You can also include your shell's code directly in the kernel by copying -relevant parts of the app's glue unit in your kernel's glue unit. This is often -simpler and more efficient. However, if your shell is a big program, it might -run into zasm's limits. In that case, you'd have to assemble your shell -separately. - -## Common features - -The folder `lib/` contains code shared in more than one apps and this has the -effect that some concepts are exactly the same in many application. They are -therefore sharing documentation, here. - -### Number literals - -There are decimal, hexadecimal and binary literals. A "straight" number is -parsed as a decimal. Hexadecimal literals must be prefixed with `0x` (`0xf4`). -Binary must be prefixed with `0b` (`0b01100110`). - -Decimals and hexadecimal are "flexible". Whether they're written in a byte or -a word, you don't need to prefix them with zeroes. Watch out for overflow, -however. - -Binary literals are also "flexible" (`0b110` is fine), but can't go over a byte. - -There is also the char literal (`'X'`), that is, two quotes with a character in -the middle. The value of that character is interpreted as-is, without any -encoding involved. That is, whatever binary code is written in between those -two quotes, it's what is evaluated. Only a single byte at once can be evaluated -thus. There is no escaping. `'''` results in `0x27`. You can't express a newline -this way, it's going to mess with the parser. - -### Expressions - -An expression is a bunch of literals or symbols assembled by operators. -Supported operators are `+`, `-`, `*`, `/`, `%` (modulo), `&` (bitwise and), -`|` (bitwise or), `^` (bitwise xor), `{` (shift left), `}` (shift right). -Bitwise operator always operate on the whole 16-bits. - -Shift operators break from the `<<` and `>>` tradition because the complexity -if two-sized operator is significant and deemed not worth it. The shift -operator shift the left operand X times, X being the right operand. - -There is no parenthesis support yet. - -Symbols have a different meaning depending on the application. In zasm, it's -labels and constants. In basic, it's variables. - -Expressions can't contain spaces. - -Expressions can have an empty left operand. It will then be considered as 0. -This allows signed integers, for example, `-42` to be expressed as expected. -That form doesn't work well everywhere and is mostly supported for BASIC. In -zasm, you're safer with `0-42`. diff --git a/apps/at28w/glue.asm b/apps/at28w/glue.asm deleted file mode 100644 index d21cd55..0000000 --- a/apps/at28w/glue.asm +++ /dev/null @@ -1,26 +0,0 @@ -; at28w - Write to AT28 EEPROM -; -; Write data from the active block device into an eeprom device geared as -; regular memory. Implements write polling to know when the next byte can be -; written and verifies that data is written properly. -; -; Optionally receives a word argument that specifies the number or bytes to -; write. If unspecified, will write until max bytes (0x2000) is reached or EOF -; is reached on the block device. - -; *** Requirements *** -; blkGetB -; -; *** Includes *** - -.inc "user.h" -.inc "err.h" -.equ AT28W_RAMSTART USER_RAMSTART - -jp at28wMain - -.inc "core.asm" -.inc "lib/util.asm" -.inc "lib/parse.asm" -.inc "at28w/main.asm" -USER_RAMSTART: diff --git a/apps/at28w/main.asm b/apps/at28w/main.asm deleted file mode 100644 index 9c22f7a..0000000 --- a/apps/at28w/main.asm +++ /dev/null @@ -1,78 +0,0 @@ -; *** Consts *** -; Memory address where the AT28 is configured to start -.equ AT28W_MEMSTART 0x2000 - -; Value mismatch during validation -.equ AT28W_ERR_MISMATCH 0x10 - -; *** Variables *** -.equ AT28W_MAXBYTES AT28W_RAMSTART -.equ AT28W_RAMEND @+2 -; *** Code *** - -at28wMain: - ld de, AT28W_MAXBYTES - ld a, (hl) - or a - jr z, at28wInner ; no arg - call parseHexadecimal ; --> DE - jr z, at28wInner - ; bad args - ld a, SHELL_ERR_BAD_ARGS - ret - -at28wInner: - ; Reminder: words in parseArgs aren't little endian. High byte is first. - ld a, (AT28W_MAXBYTES) - ld b, a - ld a, (AT28W_MAXBYTES+1) - ld c, a - ld hl, AT28W_MEMSTART - call at28wBCZero - jr nz, .loop - ; BC is zero, default to 0x2000 (8k, the size of the AT28) - ld bc, 0x2000 -.loop: - call blkGetB - jr nz, .loopend - ld (hl), a - ld e, a ; save expected data for verification - ; initiate polling - ld a, (hl) - ld d, a -.wait: - ; as long as writing operation is running, IO/6 will toggle at each - ; read attempt. We know that write is finished when we read the same - ; value twice. - ld a, (hl) - cp d - jr z, .waitend - ld d, a - jr .wait -.waitend: - - ; same value was read twice. A contains our final value for this memory - ; address. Let's compare with what we're written. - cp e - jr nz, .mismatch - inc hl - dec bc - call at28wBCZero - jr nz, .loop - -.loopend: - ; We're finished. Success! - xor a - ret - -.mismatch: - ld a, AT28W_ERR_MISMATCH - ret - -at28wBCZero: - xor a - cp b - ret nz - cp c - ret - diff --git a/apps/basic/README.md b/apps/basic/README.md deleted file mode 100644 index 4564759..0000000 --- a/apps/basic/README.md +++ /dev/null @@ -1,282 +0,0 @@ -# basic - -This is a BASIC interpreter which has been written from scratch for Collapse OS. -There are many existing z80 implementations around, some of them open source -and most of them good and efficient, but because a lot of that code overlaps -with code that has already been written for zasm, I believe that it's better to -reuse those bits of code. - -## Design goal - -The reason for including a BASIC dialect in Collapse OS is to supply some form -of system administration swiss knife. zasm, ed and the shell can do -theoretically anything, but some tasks (which are difficult to predict) can -possibly be overly tedious. One can think, for example, about hardware -debugging. Poking and peeking around when not sure what we're looking for can -be a lot more effective with the help of variables, conditions and for-loops in -an interpreter. - -Because the goal is not to provide a foundation for complex programs, I'm -planning on intentionally crippling this BASIC dialect for the sake of -simplicity. - -The idea here is that the system administrator would build herself many little -tools in assembler and BASIC would be the interactive glue to those tools. - -If you find yourself writing complex programs in Collapse OS BASIC, you're on a -wrong path. Back off, that program should be in assembler. - -## Glueing - -The `glue.asm` file in this folder represents the minimal basic system. There -are additional modules that can be added that aren't added by default, such -as `fs.asm` because they require kernel options that might not be available. - -To include these modules, you'll need to write your own glue file and to hook -extra commands through `BAS_FINDHOOK`. Look for examples in `tools/emul` and -in recipes. - -## Usage - -Upon launch, a prompt is presented, waiting for a command. There are two types -of command invocation: direct and numbered. - -A direct command is executed immediately. Example: `print 42` will print `42` -immediately. - -A numbered command is added to BASIC's code listing at the specified line -number. For example, `10 print 42` will set line 10 to the string `print 42`. - -Code listing can be printed with `list` and can be ran with `run`. The listing -is kept in order of lines. Line number don't need to be sequential. You can -keep leeway in between your lines and then insert a line with a middle number -later. - -Some commands take arguments. Those are given by typing a whitespace after the -command name and then the argument. Additional arguments are given the same way, -by typing a whitespace. - -### Numbers, expressions and variables - -Numbers are stored in memory as 16-bit integers (little endian) and numbers -being represented by BASIC are expressed as signed integers, in decimal form. -Line numbers, however, are expressed and treated as unsigned integers: You can, -if you want, put something on line "-1", but it will be the equivalent of line -65535. When expressing number literals, you can do so either in multiple forms. -See "Number literals" in `apps/README.md` for details. - -Expressions are accepted wherever a number is expected. For example, -`print 2+3` will print `5`. See "Expressions" in `apps/README.md`. - -Inside a `if` command, "truth" expressions are accepted (`=`, `<`, `>`, `<=`, -`>=`). A thruth expression that doesn't contain a truth operator evaluates the -number as-is: zero if false, nonzero is true. - -There are 26 one-letter variables in BASIC which can be assigned a 16-bit -integer to them. You assign a value to a variable with `=`. For example, -`a=42+4` will assign 46 to `a` (case insensitive). Those variables can then -be used in expressions. For example, `print a-6` will print `40`. All variables -are initialized to zero on launch. - -### Arguments - -Some commands take arguments and there are some common patterns regarding them. - -One of them is that all commands that "return" something (`input`, `peek`, -etc.) always to so in variable `A`. - -Another is that whenever a number is expected, expressions, including the ones -with variables in it, work fine. - -### One-liners - -The `:` character, when not inside a `""` literal, allows you to cram more than -one instruction on the same line. - -Things are special with `if`. All commands following a `if` are bound to that -`if`'s condition. `if 0 foo:bar` doesn't execute `bar`. - -Another special thing is `goto`. A `goto` followed by `:` will have the commands -following the `:` before the goto occurs. - -### Commands - -There are two types of commands: normal and direct-only. The latter can only -be invoked in direct mode, not through a code listing. - -`list`: Direct-only. Prints all lines in the code listing, prefixing them -with their associated line number. - -`run`: Direct-only. Runs code from the listing, starting with the first one. -If `goto` was previously called in direct mode, we start from that line instead. - -`clear`: Direct-only. Clears the current code listing. - -`print []`: Prints the result of the specified expression, -then CR/LF. Can be given multiple arguments. In that case, all arguments are -printed separately with a space in between. For example, `print 12 13` prints -`12 13` - -Unlike anywhere else, the `print` command can take a string inside a double -quote. That string will be printed as-is. For example, `print "foo" 40+2` will -print `foo 42`. - -`goto `: Make the next line to be executed the line number -specified as an argument. Errors out if line doesn't exist. Argument can be -an expression. If invoked in direct mode, `run` must be called to actually -run the line (followed by the next, and so on). - -`if `: If specified condition is true, execute the rest of the -line. Otherwise, do nothing. For example, `if 2>1 print 12` prints `12` and `if -2<1 print 12` does nothing. The argument for this command is a "thruth -expression". - -`while `: As long as specified condition is true, execute specified -commands repeatedly. - -`input []`: Prompts the user for a numerical value and puts that -value in `A`. The prompted value is evaluated as an expression and then stored. -The command takes an optional string literal parameter. If present, that string -will be printed before asking for input. Unlike a `print` call, there is no -CR/LF after that print. - -`peek/deek `: Put the value at specified memory address into `A`. peek is for -a single byte, deek is for a word (little endian). For example, `peek 42` puts -the byte value contained in memory address 0x002a into variable `A`. `deek 42` -does the same as peek, but also puts the value of 0x002b into `A`'s MSB. - -`poke/doke `: Put the value of specified expression into -specified memory address. For example, `poke 42 0x102+0x40` puts `0x42` in -memory address 0x2a (MSB is ignored) and `doke 42 0x102+0x40` does the same -as poke, but also puts `0x01` in memory address 0x2b. - -`in `: Same thing as `peek`, but for a I/O port. `in 42` generates an -input I/O on port 42 and stores the byte result in `A`. - -`out `: Same thing as `poke`, but for a I/O port. `out 42 1+2` -generates an output I/O on port 42 with value 3. - -`getc`: Waits for a single character to be typed in the console and then puts -that value in `A`. - -`putc `: Puts the specified character to the console. - -`puth `: Puts the specified character to the console, encoded in two -hexadecimal digits. For example, `puth 0x42` yields `42`. This is useful for -spitting binary contents to a console that has special handling of certain -control characters. - -`sleep `: Sleep a number of "units" specified by the supplied -expression. A "unit" depends on the CPU clock speed. At 4MHz, it is roughly 8 -microseconds. - -`addr `: This very handy returns (in `A`), the address you query for. -You can query for two types of things: commands or special stuff. - -If you query for a command, type the name of the command as an argument. The -address of the associated routine will be returned. - -Then, there's the *special stuff*. This is the list of things you can query for: - -* `$`: the scratchpad. - -`usr `: This calls the memory address specified as an expression -argument. Before doing so, it sets the registers according to a specific -logic: Variable `A`'s LSB goes in register `A`, variable `D` goes in register -`DE`, `H` in `HL` `B` in `BC` and `X` in `IX`. `IY` can't be used because -it's used for the jump. Then, after the call, the value of the registers are -put back into the variables following the same logic. - -Let's say, for example, that you want to use the kernel's `printstr` to print -the contents of the scratchpad. First, you would call `addr $` to put the -address of the scratchpad in `A`, then do `h=a` to have that address in `HL` -and, if printstr is, for example, the 21st entry in your jump table, you'd do -`usr 21*3` and see the scratchpad printed! - -## Optional modules - -As explained in "glueing" section abolve, this folder contains optional modules. -Here's the documentation for them. - -### blk - -Block devices commands. Block devices are configured during kernel -initialization and are referred to by numbers. - -`bsel `: Select the active block device. The active block device is -the target of all commands below. You select it by specifying its number. For -example, `bsel 0` selects the first configured device. `bsel 1` selects the -second. - -A freshly selected blkdev begins with its "pointer" at 0. - -`bseek `: Moves the blkdev "pointer" to the specified offset. The -first argument is the offset's least significant half (blkdev supports 32-bit -addressing). Is is interpreted as an unsigned integer. - -The second argument is optional and is the most significant half of the address. -It defaults to 0. - -`getb`: Read a byte in active blkdev at current pointer, then advance the -pointer by one. Read byte goes in `A`. - -`putb `: Writes a byte in active blkdev at current pointer, then -advance the pointer by one. The value of the byte is determined by the -expression supplied as an argument. Example: `putb 42`. - -### fs - -`fs.asm` provides those commands: - -`fls`: prints the list of files contained in the active filesystem. - -`fopen `: Open file "fname" in handle "fhandle". File handles -are specified in kernel glue code and are in limited number. The kernel glue -code also maps to blkids through the glue code. So to know what you're doing -here, you have to look at your glue code. - -In the emulated code, there are two file handles. Handle 0 maps to blkid 1 and -handle 1 maps to blkid 2. - -Once a file is opened, you can use the mapped blkid as you would with any block -device (bseek, getb, putb). - -`fnew `: Allocates space of "blkcnt" blocks (each block is -0x100 bytes in size) for a new file names "fname". Maximum blkcnt is 0xff. - -`fdel `: Mark file named "fname" as deleted. - -`ldbas `: loads the content of the file specified in the argument -(as an unquoted filename) and replace the current code listing with this -contents. Any line not starting with a number is ignored (not an error). - -`basPgmHook`: That is not a command, but a routine to hook into -`BAS_FINDHOOK`. If you do, whenever a command name isn't found, the filesystem -is iterated to see if it finds a file with the same name. If it does, it loads -its contents at `USER_CODE` (from `user.h`) and calls that address, with HL -pointing to the the remaining args in the command line. - -The user code called this way follows the *usr* convention for output, that is, -it converts all registers at the end of the call and stores them in appropriate -variables. If `A` is nonzero, an error is considered to have occurred. - -It doesn't do var-to-register transfers on input, however. Only HL is passed -through (with the contents of the command line). - -### sdc - -`sdc.asm` provides SD card related commands: - -`sdci`: initializes a SD card for operation. This should be ran whenever you -insert a new SD card. - -`sdcf`: flushes current buffers to the SD card. This is done automatically, but -only on a "needs to flush" basis, that is, when dirty buffers need to be -swapped. This command ensures that all buffers are clean (not dirty). - -### floppy - -`floppy.asm` provides TRS-80 floppy related commands: - -`flush`: Like `sdcf` above, but for floppies. Additionally, it invalidates all -buffers, allowing you to swap disks and then read proper contents. diff --git a/apps/basic/blk.asm b/apps/basic/blk.asm deleted file mode 100644 index c156bd1..0000000 --- a/apps/basic/blk.asm +++ /dev/null @@ -1,49 +0,0 @@ -basBSEL: - call rdExpr - ret nz - push ix \ pop hl - call blkSelPtr - ld a, l - jp blkSel - -basBSEEK: - call rdExpr - ret nz - push ix ; --> lvl 1 - call rdExpr - push ix \ pop de - pop hl ; <-- lvl 1 - jr z, .skip - ; DE not supplied, set to zero - ld de, 0 -.skip: - xor a ; absolute mode - call blkSeek - cp a ; ensure Z - ret - -basGETB: - call blkGetB - ret nz - ld (VAR_TBL), a - xor a - ld (VAR_TBL+1), a - ret - -basPUTB: - call rdExpr - ret nz - push ix \ pop hl - ld a, l - jp blkPutB - -basBLKCmds: - .db "bsel", 0 - .dw basBSEL - .db "bseek", 0 - .dw basBSEEK - .db "getb", 0 - .dw basGETB - .db "putb", 0 - .dw basPUTB - .db 0xff ; end of table diff --git a/apps/basic/buf.asm b/apps/basic/buf.asm deleted file mode 100644 index dee6a1b..0000000 --- a/apps/basic/buf.asm +++ /dev/null @@ -1,182 +0,0 @@ -; *** Consts *** -; maximum number of lines (line number maximum, however, is always 0xffff) -.equ BUF_MAXLINES 0x100 -; Size of the string pool -.equ BUF_POOLSIZE 0x1000 - -; *** Variables *** -; A pointer to the first free line -.equ BUF_LFREE BUF_RAMSTART -; A pointer to the first free byte in the pool -.equ BUF_PFREE @+2 -; The line index. Each record consists of 4 bytes: 2 for line number, -; 2 for pointer to string in pool. Kept in order of line numbers. -.equ BUF_LINES @+2 -; The line pool. A list of null terminated strings. BUF_LINES records point -; to those strings. -.equ BUF_POOL @+BUF_MAXLINES*4 -.equ BUF_RAMEND @+BUF_POOLSIZE - -bufInit: - ld hl, BUF_LINES - ld (BUF_LFREE), hl - ld hl, BUF_POOL - ld (BUF_PFREE), hl - cp a ; ensure Z - ret - -; Add line at (HL) with line number DE to the buffer. The string at (HL) should -; not contain the line number prefix or the whitespace between the line number -; and the comment. -; Note that an empty string is *not* an error. It will be saved as a line. -; Z for success. -; Error conditions are: -; * not enough space in the pool -; * not enough space in the line index -bufAdd: - ; Check whether we have enough pool space. This is done in all cases. - call strlen - inc a ; strlen doesn't include line termination - exx ; preserve HL and DE - ld hl, (BUF_PFREE) - call addHL - ld de, BUF_RAMEND - sbc hl, de - exx ; restore - ; no carry? HL >= BUF_RAMEND, error. Z already unset - ret nc - - ; Check the kind of operation we make: add, insert or replace? - call bufFind - jr z, .replace ; exact match, replace - call c, .insert ; near match, insert - - ; do we have enough index space? - exx ; preserve HL and DE - ld hl, (BUF_LFREE) - ld de, BUF_POOL-4 - or a ; reset carry - sbc hl, de - exx ; restore - ; no carry? HL >= BUF_POOL, error. Z already unset - ret nc - - ; We have enough space. - ; set line index data - push de ; --> lvl 1 - ld (ix), e - ld (ix+1), d - ld de, (BUF_PFREE) - ld (ix+2), e - ld (ix+3), d - - ; Increase line index size - ld de, (BUF_LFREE) - inc de \ inc de \ inc de \ inc de - ld (BUF_LFREE), de - - ; Fourth step: copy string to pool - ld de, (BUF_PFREE) - call strcpyM - ld (BUF_PFREE), de - pop de ; <-- lvl 1 - ret - -; No need to add a new line, just replace the current one. -.replace: - ld (ix), e - ld (ix+1), d - push de - ld de, (BUF_PFREE) - ld (ix+2), e - ld (ix+3), d - call strcpyM - ld (BUF_PFREE), de - pop de - ret - -; An insert is exactly like an add, except that lines following insertion point -; first. -.insert: - push hl - push de - push bc - ; We want a LDDR that moves from (BUF_LFREE)-1 to (BUF_LFREE)+3 - ; for a count of (BUF_LFREE)-BUF_LINES - ld hl, (BUF_LFREE) - ld de, BUF_LINES - or a ; clear carry - sbc hl, de - ld b, h - ld c, l - ld hl, (BUF_LFREE) - ld d, h - ld e, l - dec hl - inc de \ inc de \ inc de - lddr - pop bc - pop de - pop hl - ret - -; Set IX to point to the beginning of the pool. -; Z set if (IX) is a valid line, unset if the pool is empty. -bufFirst: - ld ix, BUF_LINES - jp bufEOF - -; Given a valid line record in IX, move IX to the next line. -; This routine doesn't check that IX is valid. Ensure IX validity before -; calling. -bufNext: - inc ix \ inc ix \ inc ix \ inc ix - jp bufEOF - -; Returns whether line index at IX is past the end of file, that is, -; whether IX == (BUF_LFREE) -; Z is set when not EOF, unset when EOF. -bufEOF: - push hl - push de - push ix \ pop hl - or a ; clear carry - ld de, (BUF_LFREE) - sbc hl, de - jr z, .empty - cp a ; ensure Z -.end: - pop de - pop hl - ret -.empty: - call unsetZ - jr .end - -; Given a line index in (IX), set HL to its associated string pointer. -bufStr: - ld l, (ix+2) - ld h, (ix+3) - ret - -; Browse lines looking for number DE. Set IX to point to one of these : -; 1 - an exact match -; 2 - the first found line to have a higher line number -; 3 - EOF -; Set Z on an exact match, C on a near match, NZ and NC on EOF. -bufFind: - call bufFirst - ret nz -.loop: - ld a, d - cp (ix+1) - ret c ; D < (IX+1), situation 2 - jr nz, .next - ld a, e - cp (ix) - ret c ; E < (IX), situation 2 - ret z ; exact match! -.next: - call bufNext - ret nz - jr .loop diff --git a/apps/basic/floppy.asm b/apps/basic/floppy.asm deleted file mode 100644 index 49c7108..0000000 --- a/apps/basic/floppy.asm +++ /dev/null @@ -1,10 +0,0 @@ -; floppy-related basic commands - -basFLUSH: - jp floppyFlush - -basFloppyCmds: - .db "flush", 0 - .dw basFLUSH - .db 0xff ; end of table - diff --git a/apps/basic/fs.asm b/apps/basic/fs.asm deleted file mode 100644 index 1272f60..0000000 --- a/apps/basic/fs.asm +++ /dev/null @@ -1,140 +0,0 @@ -; FS-related basic commands -; *** Variables *** -; Handle of the target file -.equ BFS_FILE_HDL BFS_RAMSTART -.equ BFS_RAMEND @+FS_HANDLE_SIZE - -; Lists filenames in currently active FS -basFLS: - ld iy, .iter - jp fsIter -.iter: - ld a, FS_META_FNAME_OFFSET - call addHL - call printstr - jp printcrlf - - -basLDBAS: - call fsFindFN - ret nz - call bufInit - ld ix, BFS_FILE_HDL - call fsOpen - ld hl, 0 - ld de, SCRATCHPAD -.loop: - ld ix, BFS_FILE_HDL - call fsGetB - jr nz, .loopend - inc hl - or a ; null? hum, weird. same as LF - jr z, .lineend - cp LF - jr z, .lineend - ld (de), a - inc de - jr .loop -.lineend: - ; We've just finished reading a line, writing each char in the pad. - ; Null terminate it. - xor a - ld (de), a - ; Ok, line ready - push hl ; --> lvl 1. current file position - ld hl, SCRATCHPAD - call parseDecimalC - jr nz, .notANumber - call rdSep - call bufAdd - pop hl ; <-- lvl 1 - ret nz - ld de, SCRATCHPAD - jr .loop -.notANumber: - pop hl ; <-- lvl 1 - ld de, SCRATCHPAD - jr .loop -.loopend: - cp a - ret - - -basFOPEN: - call rdExpr ; file handle index - ret nz - push ix \ pop de - ld a, e - call fsHandle - ; DE now points to file handle - call rdSep - ; HL now holds the string we look for - call fsFindFN - ret nz ; not found - ; Found! - ; FS_PTR points to the file we want to open - push de \ pop ix ; IX now points to the file handle. - jp fsOpen - -; Takes one byte block number to allocate as well we one string arg filename -; and allocates a new file in the current fs. -basFNEW: - call rdExpr ; file block count - ret nz - call rdSep ; HL now points to filename - push ix \ pop de - ld a, e - jp fsAlloc - -; Deletes filename with specified name -basFDEL: - call fsFindFN - ret nz - ; Found! delete - jp fsDel - - -basPgmHook: - ; Cmd to find is in (DE) - ex de, hl - ; (HL) is suitable for a direct fsFindFN call - call fsFindFN - ret nz - ; We have a file! Let's load it in memory - ld ix, BFS_FILE_HDL - call fsOpen - ld hl, 0 ; addr that we read in file handle - ld de, USER_CODE ; addr in mem we write to -.loop: - call fsGetB ; we use Z at end of loop - ld (de), a ; Z preserved - inc hl ; Z preserved in 16-bit - inc de ; Z preserved in 16-bit - jr z, .loop - ; Ready to jump. Return .call in IX and basCallCmd will take care - ; of setting (HL) to the arg string. .call then takes care of wrapping - ; the USER_CODE call. - ld ix, .call - cp a ; ensure Z - ret -.call: - ld iy, USER_CODE - call callIY - call basR2Var - or a ; Z set only if A is zero - ret - -basFSCmds: - .db "fls", 0 - .dw basFLS - .db "ldbas", 0 - .dw basLDBAS - .db "fopen", 0 - .dw basFOPEN - .db "fnew", 0 - .dw basFNEW - .db "fdel", 0 - .dw basFDEL - .db "fson", 0 - .dw fsOn - .db 0xff ; end of table diff --git a/apps/basic/glue.asm b/apps/basic/glue.asm deleted file mode 100644 index b846746..0000000 --- a/apps/basic/glue.asm +++ /dev/null @@ -1,33 +0,0 @@ -; *** Requirements *** -; printstr -; printcrlf -; stdioReadLine -; strncmp -; -.inc "user.h" -.inc "err.h" - - call basInit - jp basStart - -; RAM space used in different routines for short term processing. -.equ SCRATCHPAD_SIZE 0x20 -.equ SCRATCHPAD USER_RAMSTART - -.inc "core.asm" -.inc "lib/util.asm" -.inc "lib/ari.asm" -.inc "lib/parse.asm" -.inc "lib/fmt.asm" -.equ EXPR_PARSE parseLiteralOrVar -.inc "lib/expr.asm" -.inc "basic/util.asm" -.inc "basic/parse.asm" -.inc "basic/tok.asm" -.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE -.inc "basic/var.asm" -.equ BUF_RAMSTART VAR_RAMEND -.inc "basic/buf.asm" -.equ BAS_RAMSTART BUF_RAMEND -.inc "basic/main.asm" -USER_RAMSTART: diff --git a/apps/basic/main.asm b/apps/basic/main.asm deleted file mode 100644 index 34fea33..0000000 --- a/apps/basic/main.asm +++ /dev/null @@ -1,531 +0,0 @@ -; *** Variables *** -; Value of `SP` when basic was first invoked. This is where SP is going back to -; on restarts. -.equ BAS_INITSP BAS_RAMSTART -; Pointer to next line to run. If nonzero, it means that the next line is -; the first of the list. This is used by GOTO to indicate where to jump next. -; Important note: this is **not** a line number, it's a pointer to a line index -; in buffer. If it's not zero, its a valid pointer. -.equ BAS_PNEXTLN @+2 -; Points to a routine to call when a command isn't found in the "core" cmd -; table. This gives the opportunity to glue code to configure extra commands. -.equ BAS_FINDHOOK @+2 -.equ BAS_RAMEND @+2 - -; *** Code *** -basInit: - ld (BAS_INITSP), sp - call varInit - call bufInit - xor a - ld (BAS_PNEXTLN), a - ld (BAS_PNEXTLN+1), a - ld hl, unsetZ - ld (BAS_FINDHOOK), hl - ret - -basStart: - ld hl, .welcome - call printstr - call printcrlf - jr basLoop - -.welcome: - .db "Collapse OS", 0 - -basLoop: - ld hl, .sPrompt - call printstr - call stdioReadLine - call printcrlf - call parseDecimalC - jr z, .number - ld de, basCmds1 - call basCallCmds - jr z, basLoop - ; Error - call basERR - jr basLoop -.number: - call rdSep - call bufAdd - jp nz, basERR - jr basLoop -.sPrompt: - .db "> ", 0 - -; Tries to find command specified in (DE) (must be null-terminated) in cmd -; table in (HL). If found, sets IX to point to the associated routine. If -; not found, calls BAS_FINDHOOK so that we look through extra commands -; configured by glue code. -; Destroys HL. -; Z is set if found, unset otherwise. -basFindCmd: -.loop: - call strcmp - call strskip - inc hl ; point to routine - jr z, .found ; Z from strcmp - inc hl \ inc hl ; skip routine - ld a, (hl) - inc a ; was it 0xff? - jr nz, .loop ; no - dec a ; unset Z - ret -.found: - call intoHL - push hl \ pop ix - ret - -; Call command in (HL) after having looked for it in cmd table in (DE). -; If found, jump to it. If not found, try (BAS_FINDHOOK). If still not found, -; unset Z. We expect commands to set Z on success. Therefore, when calling -; basCallCmd results in NZ, we're not sure where the error come from, but -; well... -basCallCmd: - ; let's see if it's a variable assignment. - call varTryAssign - ret z ; Done! - push de ; --> lvl 1. - ld de, SCRATCHPAD - call rdWord - ; cmdname to find in (DE) - ; How lucky, we have a legitimate use of "ex (sp), hl"! We have the - ; cmd table in the stack, which we want in HL and we have the rest of - ; the cmdline in (HL), which we want in the stack! - ex (sp), hl - call basFindCmd - jr z, .skip - ; not found, try BAS_FINDHOOK - ld ix, (BAS_FINDHOOK) - call callIX -.skip: - ; regardless of the result, we need to balance the stack. - ; Bring back rest of the command string from the stack - pop hl ; <-- lvl 1 - ret nz - ; cmd found, skip whitespace and then jump! - call rdSep - jp (ix) - -; Call a series of ':'-separated commands in (HL) using cmd table in (DE). -; Stop processing as soon as one command unsets Z. -basCallCmds: - ; Commands are not guaranteed at all to preserve HL and DE, so we - ; preserve them ourselves here. - push hl ; --> lvl 1 - push de ; --> lvl 2 - call basCallCmd - pop de ; <-- lvl 2 - pop hl ; <-- lvl 1 - ret nz - call toEnd - ret z ; no more cmds - ; we met a ':', we have more cmds - inc hl - call basCallCmds - ; move the the end of the string so that we don't run cmds following a - ; ':' twice. - call strskip - ret - -basERR: - ld hl, .sErr - call printstr - jp printcrlf -.sErr: - .db "ERR", 0 - -; *** Commands *** -; A command receives its argument through (HL), which is already placed to -; either: -; 1 - the end of the string if the command has no arg. -; 2 - the beginning of the arg, with whitespace properly skipped. -; -; Commands are expected to set Z on success. -basLIST: - call bufFirst - jr nz, .end -.loop: - ld e, (ix) - ld d, (ix+1) - ld hl, SCRATCHPAD - call fmtDecimal - call printstr - ld a, ' ' - call stdioPutC - call bufStr - call printstr - call printcrlf - call bufNext - jr z, .loop -.end: - cp a ; ensure Z - ret - - -basRUN: - call .maybeGOTO - jr nz, .loop ; IX already set - call bufFirst - ret nz -.loop: - call bufStr - ld de, basCmds2 - push ix ; --> lvl 1 - call basCallCmds - pop ix ; <-- lvl 1 - jp nz, .err - call .maybeGOTO - jr nz, .loop ; IX already set - call bufNext - jr z, .loop - cp a ; ensure Z - ret -.err: - ; Print line number, then return NZ (which will print ERR) - ld e, (ix) - ld d, (ix+1) - ld hl, SCRATCHPAD - call fmtDecimal - call printstr - ld a, ' ' - call stdioPutC - jp unsetZ - -; This returns the opposite Z result as the one we usually see: Z is set if -; we **don't** goto, unset if we do. If we do, IX is properly set. -.maybeGOTO: - ld de, (BAS_PNEXTLN) - ld a, d - or e - ret z - ; we goto - push de \ pop ix - ; we need to reset our goto marker - ld de, 0 - ld (BAS_PNEXTLN), de - ret - -basPRINT: - ; Do we have arguments at all? if not, it's not an error, just print - ; crlf - ld a, (hl) - or a - jr z, .end - ; Is our arg a string literal? - call spitQuoted - jr z, .chkAnother ; string printed, skip to chkAnother - ld de, SCRATCHPAD - call rdWord - push hl ; --> lvl 1 - ex de, hl - call parseExpr - jr nz, .parseError - ld hl, SCRATCHPAD - call fmtDecimalS - call printstr - pop hl ; <-- lvl 1 -.chkAnother: - ; Do we have another arg? - call rdSep - jr z, .another - ; no, we can stop here -.end: - cp a ; ensure Z - jp printcrlf -.another: - ; Before we jump to basPRINT, let's print a space - ld a, ' ' - call stdioPutC - jr basPRINT -.parseError: - ; unwind the stack before returning - pop hl ; <-- lvl 1 - ret - - -basGOTO: - ld de, SCRATCHPAD - call rdWord - ex de, hl - call parseExpr - ret nz - call bufFind - jr nz, .notFound - push ix \ pop de - ; Z already set - jr .end -.notFound: - ld de, 0 - ; Z already unset -.end: - ld (BAS_PNEXTLN), de - ret - -; evaluate truth condition at (HL) and set A to its value -; Z for success (but not truth!) -_basEvalCond: - push hl ; --> lvl 1. original arg - ld de, SCRATCHPAD - call rdWord - ex de, hl - call parseTruth - pop hl ; <-- lvl 1. restore - ret - -basIF: - call _basEvalCond - ret nz ; error - or a - ret z - ; expr is true, execute next - ; (HL) back to beginning of args, skip to next arg - call toSepOrEnd - call rdSep - ret nz - ld de, basCmds2 - jp basCallCmds - -basWHILE: - push hl ; --> lvl 1 - call _basEvalCond - jr nz, .stop ; error - or a - jr z, .stop - ret z - ; expr is true, execute next - ; (HL) back to beginning of args, skip to next arg - call toSepOrEnd - call rdSep - ret nz - ld de, basCmds2 - call basCallCmds - pop hl ; <-- lvl 1 - jr basWHILE -.stop: - pop hl ; <-- lvl 1 - ret - -basINPUT: - ; If our first arg is a string literal, spit it - call spitQuoted - call rdSep - call stdioReadLine - call parseExpr - ld (VAR_TBL), de - call printcrlf - cp a ; ensure Z - ret - -basPEEK: - call basDEEK - ret nz - ; set MSB to 0 - xor a ; sets Z - ld (VAR_TBL+1), a - ret - -basPOKE: - call rdExpr - ret nz - ; peek address in IX. Save it for later - push ix ; --> lvl 1 - call rdSep - call rdExpr - push ix \ pop hl - pop ix ; <-- lvl 1 - ret nz - ; Poke! - ld (ix), l - ret - -basDEEK: - call rdExpr - ret nz - ; peek address in IX. Let's peek and put result in DE - ld e, (ix) - ld d, (ix+1) - ld (VAR_TBL), de - cp a ; ensure Z - ret - -basDOKE: - call basPOKE - ld (ix+1), h - ret - -basOUT: - call rdExpr - ret nz - ; out address in IX. Save it for later - push ix ; --> lvl 1 - call rdSep - call rdExpr - push ix \ pop hl - pop bc ; <-- lvl 1 - ret nz - ; Out! - out (c), l - cp a ; ensure Z - ret - -basIN: - call rdExpr - ret nz - push ix \ pop bc - ld d, 0 - in e, (c) - ld (VAR_TBL), de - ; Z set from rdExpr - ret - -basGETC: - call stdioGetC - ld (VAR_TBL), a - xor a - ld (VAR_TBL+1), a - ret - -basPUTC: - call rdExpr - ret nz - push ix \ pop hl - ld a, l - call stdioPutC - xor a ; set Z - ret - -basPUTH: - call rdExpr - ret nz - push ix \ pop hl - ld a, l - call printHex - xor a ; set Z - ret - -basSLEEP: - call rdExpr - ret nz - push ix \ pop hl -.loop: - ld a, h ; 4T - or l ; 4T - ret z ; 5T - dec hl ; 6T - jr .loop ; 12T - -basADDR: - call rdWord - ex de, hl - ld de, .specialTbl -.loop: - ld a, (de) - or a - jr z, .notSpecial - cp (hl) - jr z, .found - inc de \ inc de \ inc de - jr .loop -.notSpecial: - ; not found, find cmd. needle in (HL) - ex de, hl ; now in (DE) - ld hl, basCmds1 - call basFindCmd - jr z, .foundCmd - ; no core command? let's try the find hook. - ld ix, (BAS_FINDHOOK) - call callIX - ret nz -.foundCmd: - ; We have routine addr in IX - ld (VAR_TBL), ix - cp a ; ensure Z - ret -.found: - ; found special thing. Put in "A". - inc de - call intoDE - ld (VAR_TBL), de - ret ; Z set from .found jump. - -.specialTbl: - .db '$' - .dw SCRATCHPAD - .db 0 - -basUSR: - call rdExpr - ret nz - push ix \ pop iy - ; We have our address to call. Now, let's set up our registers. - ; HL comes from variable H. H's index is 7*2. - ld hl, (VAR_TBL+14) - ; DE comes from variable D. D's index is 3*2 - ld de, (VAR_TBL+6) - ; BC comes from variable B. B's index is 1*2 - ld bc, (VAR_TBL+2) - ; IX comes from variable X. X's index is 23*2 - ld ix, (VAR_TBL+46) - ; and finally, A - ld a, (VAR_TBL) - call callIY -basR2Var: ; Just send reg to vars. Used in basPgmHook - ; Same dance, opposite way - ld (VAR_TBL), a - ld (VAR_TBL+46), ix - ld (VAR_TBL+2), bc - ld (VAR_TBL+6), de - ld (VAR_TBL+14), hl - cp a ; USR never errors out - ret - -; Command table format: Null-terminated string followed by a 2-byte routine -; pointer. - -; direct only -basCmds1: - .db "list", 0 - .dw basLIST - .db "run", 0 - .dw basRUN - .db "clear", 0 - .dw bufInit -; statements -basCmds2: - .db "print", 0 - .dw basPRINT - .db "goto", 0 - .dw basGOTO - .db "if", 0 - .dw basIF - .db "while", 0 - .dw basWHILE - .db "input", 0 - .dw basINPUT - .db "peek", 0 - .dw basPEEK - .db "poke", 0 - .dw basPOKE - .db "deek", 0 - .dw basDEEK - .db "doke", 0 - .dw basDOKE - .db "out", 0 - .dw basOUT - .db "in", 0 - .dw basIN - .db "getc", 0 - .dw basGETC - .db "putc", 0 - .dw basPUTC - .db "puth", 0 - .dw basPUTH - .db "sleep", 0 - .dw basSLEEP - .db "addr", 0 - .dw basADDR - .db "usr", 0 - .dw basUSR - .db 0xff ; end of table diff --git a/apps/basic/parse.asm b/apps/basic/parse.asm deleted file mode 100644 index 6af0a50..0000000 --- a/apps/basic/parse.asm +++ /dev/null @@ -1,142 +0,0 @@ -; Parse an expression yielding a truth value from (HL) and set A accordingly. -; 0 for False, nonzero for True. -; How it evaluates truth is that it looks for =, <, >, >= or <= in (HL) and, -; if it finds it, evaluate left and right expressions separately. Then it -; compares both sides and set A accordingly. -; If comparison operators aren't found, the whole string is sent to parseExpr -; and zero means False, nonzero means True. -; **This routine mutates (HL).** -; Z for success. -parseTruth: - push ix - push de - ld a, '=' - call .maybeFind - jr z, .foundEQ - ld a, '<' - call .maybeFind - jr z, .foundLT - ld a, '>' - call .maybeFind - jr z, .foundGT - jr .simple -.success: - cp a ; ensure Z -.end: - pop de - pop ix - ret - -.maybeFind: - push hl ; --> lvl 1 - call findchar - jr nz, .notFound - ; found! We want to keep new HL around. Let's pop old HL in DE - pop de ; <-- lvl 1 - ret -.notFound: - ; not found, restore HL - pop hl ; <-- lvl 1 - ret - -.simple: - call parseExpr - jr nz, .end - ld a, d - or e - jr .success - -.foundEQ: - ; we found an '=' char and HL is pointing to it. DE is pointing to the - ; beginning of our string. Let's separate those two strings. - ; But before we do that, to we have a '<' or a '>' at the left of (HL)? - dec hl - ld a, (hl) - cp '<' - jr z, .foundLTE - cp '>' - jr z, .foundGTE - inc hl - ; Ok, we are a straight '='. Proceed. - call .splitLR - ; HL now point to right-hand, DE to left-hand - call .parseLeftRight - jr nz, .end ; error, stop - xor a ; clear carry and prepare value for False - sbc hl, de - jr nz, .success ; NZ? equality not met. A already 0, return. - ; Z? equality met, make A=1, set Z - inc a - jr .success - -.foundLTE: - ; Almost the same as '<', but we have two sep chars - call .splitLR - inc hl ; skip the '=' char - call .parseLeftRight - jr nz, .end - ld a, 1 ; prepare for True - sbc hl, de - jr nc, .success ; Left <= Right, True - ; Left > Right, False - dec a - jr .success - -.foundGTE: - ; Almost the same as '<=' - call .splitLR - inc hl ; skip the '=' char - call .parseLeftRight - jr nz, .end - ld a, 1 ; prepare for True - sbc hl, de - jr z, .success ; Left == Right, True - jr c, .success ; Left > Right, True - ; Left < Right, False - dec a - jr .success - -.foundLT: - ; Same thing as EQ, but for '<' - call .splitLR - call .parseLeftRight - jr nz, .end - xor a - sbc hl, de - jr z, .success ; Left == Right, False - jr c, .success ; Left > Right, False - ; Left < Right, True - inc a - jr .success - -.foundGT: - ; Same thing as EQ, but for '>' - call .splitLR - call .parseLeftRight - jr nz, .end - xor a - sbc hl, de - jr nc, .success ; Left <= Right, False - ; Left > Right, True - inc a - jr .success - -.splitLR: - xor a - ld (hl), a - inc hl - ret - -; Given string pointers in (HL) and (DE), evaluate those two expressions and -; place their corresponding values in HL and DE. -.parseLeftRight: - ; let's start with HL - push de ; --> lvl 1 - call parseExpr - pop hl ; <-- lvl 1, orig DE - ret nz - push de ; --> lvl 1. save HL value in stack. - ; Now, for DE. (DE) is now in HL - call parseExpr ; DE in place - pop hl ; <-- lvl 1. restore saved HL - ret diff --git a/apps/basic/sdc.asm b/apps/basic/sdc.asm deleted file mode 100644 index d678c0c..0000000 --- a/apps/basic/sdc.asm +++ /dev/null @@ -1,14 +0,0 @@ -; SDC-related basic commands - -basSDCI: - jp sdcInitializeCmd - -basSDCF: - jp sdcFlushCmd - -basSDCCmds: - .db "sdci", 0 - .dw basSDCI - .db "sdcf", 0 - .dw basSDCF - .db 0xff ; end of table diff --git a/apps/basic/tok.asm b/apps/basic/tok.asm deleted file mode 100644 index 3631a59..0000000 --- a/apps/basic/tok.asm +++ /dev/null @@ -1,97 +0,0 @@ -; Whether A is a separator or end-of-string (null or ':') -isSepOrEnd: - or a - ret z - cp ':' - ret z - ; continue to isSep - -; Sets Z is A is ' ' or '\t' (whitespace) -isSep: - cp ' ' - ret z - cp 0x09 - ret - -; Expect at least one whitespace (0x20, 0x09) at (HL), and then advance HL -; until a non-whitespace character is met. -; HL is advanced to the first non-whitespace char. -; Sets Z on success, unset on failure. -; Failure is either not having a first whitespace or reaching the end of the -; string. -; Sets Z if we found a non-whitespace char, unset if we found the end of string. -rdSep: - ld a, (hl) - call isSep - ret nz ; failure -.loop: - inc hl - ld a, (hl) - call isSep - jr z, .loop - call isSepOrEnd - jp z, .fail ; unexpected EOL. fail - cp a ; ensure Z - ret -.fail: - ; A is zero at this point - inc a ; unset Z - ret - -; Advance HL to the next separator or to the end of string. -toSepOrEnd: - ld a, (hl) - call isSepOrEnd - ret z - inc hl - jr toSepOrEnd - -; Advance HL to the end of the line, that is, either a null terminating char -; or the ':'. -; Sets Z if we met a null char, unset if we met a ':' -toEnd: - ld a, (hl) - or a - ret z - cp ':' - jr z, .havesep - inc hl - call skipQuoted - jr toEnd -.havesep: - inc a ; unset Z - ret - -; Read (HL) until the next separator and copy it in (DE) -; DE is preserved, but HL is advanced to the end of the read word. -rdWord: - push af - push de -.loop: - ld a, (hl) - call isSepOrEnd - jr z, .stop - ld (de), a - inc hl - inc de - jr .loop -.stop: - xor a - ld (de), a - pop de - pop af - ret - -; Read word from HL in SCRATCHPAD and then intepret that word as an expression. -; Put the result in IX. -; Z for success. -; TODO: put result in DE -rdExpr: - ld de, SCRATCHPAD - call rdWord - push hl - ex de, hl - call parseExpr - push de \ pop ix - pop hl - ret diff --git a/apps/basic/util.asm b/apps/basic/util.asm deleted file mode 100644 index 0c2f831..0000000 --- a/apps/basic/util.asm +++ /dev/null @@ -1,32 +0,0 @@ -; Is (HL) a double-quoted string? If yes, spit what's inside and place (HL) -; at char after the closing quote. -; Set Z if there was a string, unset otherwise. -spitQuoted: - ld a, (hl) - cp '"' - ret nz - inc hl -.loop: - ld a, (hl) - inc hl - cp '"' - ret z - or a - ret z - call stdioPutC - jr .loop - -; Same as spitQuoted, but without the spitting -skipQuoted: - ld a, (hl) - cp '"' - ret nz - inc hl -.loop: - ld a, (hl) - inc hl - cp '"' - ret z - or a - ret z - jr .loop diff --git a/apps/basic/var.asm b/apps/basic/var.asm deleted file mode 100644 index 8332e78..0000000 --- a/apps/basic/var.asm +++ /dev/null @@ -1,104 +0,0 @@ -; *** Variables *** -; A list of words for each member of the A-Z range. -.equ VAR_TBL VAR_RAMSTART -.equ VAR_RAMEND @+52 - -; *** Code *** - -varInit: - ld b, VAR_RAMEND-VAR_RAMSTART - ld hl, VAR_RAMSTART - xor a -.loop: - ld (hl), a - inc hl - djnz .loop - ret - -; Check if A is a valid variable letter (a-z or A-Z). If it is, set A to a -; valid VAR_TBL index and set Z. Otherwise, unset Z (and A is destroyed) -varChk: - call upcase - sub 'A' - ret c ; Z unset - cp 27 ; 'Z' + 1 - jr c, .isVar - ; A > 'Z' - dec a ; unset Z - ret -.isVar: - cp a ; set Z - ret - -; Try to interpret line at (HL) and see if it's a variable assignment. If it -; is, proceed with the assignment and set Z. Otherwise, NZ. -varTryAssign: - inc hl - ld a, (hl) - dec hl - cp '=' - ret nz - ld a, (hl) - call varChk - ret nz - ; We have a variable! Its table index is currently in A. - push ix ; --> lvl 1 - push hl ; --> lvl 2 - push de ; --> lvl 3 - push af ; --> lvl 4. save for later - ; Let's put that expression to read in scratchpad - inc hl \ inc hl - ld de, SCRATCHPAD - call rdWord - ex de, hl - ; Now, evaluate that expression now in (HL) - call parseExpr ; --> number in DE - jr nz, .exprErr - pop af ; <-- lvl 4 - call varAssign - xor a ; ensure Z -.end: - pop de ; <-- lvl 3 - pop hl ; <-- lvl 2 - pop ix ; <-- lvl 1 - ret -.exprErr: - pop af ; <-- lvl 4 - jr .end - -; Given a variable **index** in A (call varChk to transform) and a value in -; DE, assign that value in the proper cell in VAR_TBL. -; No checks are made. -varAssign: - push hl - add a, a ; * 2 because each element is a word - ld hl, VAR_TBL - call addHL - ; HL placed, write number - ld (hl), e - inc hl - ld (hl), d - pop hl - ret - -; Check if value at (HL) is a variable. If yes, returns its associated value. -; Otherwise, jump to parseLiteral. -parseLiteralOrVar: - call isLiteralPrefix - jp z, parseLiteral - ; not a literal, try var - ld a, (hl) - call varChk - ret nz - ; It's a variable, resolve! - add a, a ; * 2 because each element is a word - push hl ; --> lvl 1 - ld hl, VAR_TBL - call addHL - ld e, (hl) - inc hl - ld d, (hl) - pop hl ; <-- lvl 1 - inc hl ; point to char after variable - cp a ; ensure Z - ret diff --git a/apps/ed/README.md b/apps/ed/README.md deleted file mode 100644 index 11d586d..0000000 --- a/apps/ed/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# ed - line editor - -Collapse OS's `ed` is modeled after UNIX's ed (let's call it `Ued`). The goal -is to have an editor that is tight on resources and that doesn't require -ncurses-like screen management. - -In general, we try to follow `Ued`'s conventions and the "Usage" section is -mostly a repeat of `Ued`'s man page. - -## Differences - -There are a couple of differences with `Ued` that are intentional. Differences -not listed here are either bugs or simply aren't implemented yet. - -* Always has a prompt, `:`. -* No size printing on load -* Initial line is the first one -* Line input is for one line at once. Less scriptable for `Ued`, but we can't - script `ed` in Collapse OS anyway... -* For the sake of code simplicity, some commands that make no sense are - accepted. For example, `1,2a` is the same as `2a`. - -## Usage - -`ed` is invoked from the shell with a single argument: the name of the file to -edit. If the file doesn't exist, `ed` errors out. If it exists, a prompt is -shown. - -In normal mode, `ed` waits for a command and executes it. If the command is -invalid, a line with `?` is printed and `ed` goes back to waiting for a command. - -A command can be invalid because it is unknown, malformed or if its address -range is out of bounds. - -### Commands - -* `(addrs)p`: Print lines specified in `addrs` range. This is the default - command. If only `(addrs)` is specified, it has the same effect. -* `(addrs)d`: Delete lines specified in `addrs` range. -* `(addr)a`: Appends a line after `addr`. -* `(addr)i`: Insert a line before `addr`. -* `w`: write to file. For now, `q` is implied in `w`. -* `q`: quit `ed` without writing to file. - -### Current line - -The current line is central to `ed`. Address ranges can be expressed relatively -to it and makes the app much more usable. The current line starts at `1` and -every command changes the current line to the last line that the command -affects. For example, `42p` changes the current line to `42`, `3,7d`, to 7. - -### Addresses - -An "address" is a line number. The first line is `1`. An address range is a -start line and a stop line, expressed as `start,stop`. For example, `2,4` refer -to lines 2, 3 and 4. - -When expressing ranges, `stop` can be omitted. It will then have the same value -as `start`. `42` is equivalent to `42,42`. - -Addresses can be expressed relatively to the current line with `+` and `-`. -`+3` means "current line + 3", `-5, +2` means "address range starting at 5 -lines before current line and ending 2 lines after it`. - -`+` alone means `+1`, `-` means `-1`. - -`.` means current line. It can usually be omitted. `p` is the same as `.p`. - -`$` means the last line of the buffer. diff --git a/apps/ed/buf.asm b/apps/ed/buf.asm deleted file mode 100644 index 1e8ce77..0000000 --- a/apps/ed/buf.asm +++ /dev/null @@ -1,211 +0,0 @@ -; buf - manage line buffer -; -; *** Variables *** -; Number of lines currently in the buffer -.equ BUF_LINECNT BUF_RAMSTART -; List of pointers to strings in scratchpad -.equ BUF_LINES @+2 -; Points to the end of the scratchpad, that is, one byte after the last written -; char in it. -.equ BUF_PADEND @+ED_BUF_MAXLINES*2 -; The in-memory scratchpad -.equ BUF_PAD @+2 - -.equ BUF_RAMEND @+ED_BUF_PADMAXLEN - -; *** Code *** - -; On initialization, we read the whole contents of target blkdev and add lines -; as we go. -bufInit: - ld hl, BUF_PAD ; running pointer to end of pad - ld de, BUF_PAD ; points to beginning of current line - ld ix, BUF_LINES ; points to current line index - ld bc, 0 ; line count - ; init pad end in case we have an empty file. - ld (BUF_PADEND), hl -.loop: - call ioGetB - jr nz, .loopend - or a ; null? hum, weird. same as LF - jr z, .lineend - cp 0x0a - jr z, .lineend - ld (hl), a - inc hl - jr .loop -.lineend: - ; We've just finished reading a line, writing each char in the pad. - ; Null terminate it. - xor a - ld (hl), a - inc hl - ; Now, let's register its pointer in BUF_LINES - ld (ix), e - inc ix - ld (ix), d - inc ix - inc bc - ld (BUF_PADEND), hl - ld de, (BUF_PADEND) - jr .loop -.loopend: - ld (BUF_LINECNT), bc - ret - -; transform line index HL into its corresponding memory address in BUF_LINES -; array. -bufLineAddr: - push de - ex de, hl - ld hl, BUF_LINES - add hl, de - add hl, de ; twice, because two bytes per line - pop de - ret - -; Read line number specified in HL and make HL point to its contents. -; Sets Z on success, unset if out of bounds. -bufGetLine: - push de ; --> lvl 1 - ld de, (BUF_LINECNT) - call cpHLDE - pop de ; <-- lvl 1 - jp nc, unsetZ ; HL > (BUF_LINECNT) - call bufLineAddr - ; HL now points to an item in BUF_LINES. - call intoHL - ; Now, HL points to our contents - cp a ; ensure Z - ret - -; Given line indexes in HL and DE where HL < DE < CNT, move all lines between -; DE and CNT by an offset of DE-HL. Also, adjust BUF_LINECNT by DE-HL. -; WARNING: no bounds check. The only consumer of this routine already does -; bounds check. -bufDelLines: - ; Let's start with setting up BC, which is (CNT-DE) * 2 - push hl ; --> lvl 1 - ld hl, (BUF_LINECNT) - scf \ ccf - sbc hl, de - ; mult by 2 and we're done - sla l \ rl h - push hl \ pop bc - pop hl ; <-- lvl 1 - ; Good! BC done. Now, let's adjust BUF_LINECNT by DE-HL - push hl ; --> lvl 1 - scf \ ccf - sbc hl, de ; HL -> nb of lines to delete, negative - push de ; --> lvl 2 - ld de, (BUF_LINECNT) - add hl, de ; adding DE to negative HL - ld (BUF_LINECNT), hl - pop de ; <-- lvl 2 - pop hl ; <-- lvl 1 - ; Line count updated! - ; One other thing... is BC zero? Because if it is, then we shouldn't - ; call ldir (otherwise we're on for a veeeery long loop), BC=0 means - ; that only last lines were deleted. nothing to do. - ld a, b - or c - ret z ; BC==0, return - - ; let's have invert HL and DE to match LDIR's signature - ex de, hl - ; At this point we have higher index in HL, lower index in DE and number - ; of bytes to delete in BC. It's convenient because it's rather close - ; to LDIR's signature! The only thing we need to do now is to translate - ; those HL and DE indexes in memory addresses, that is, multiply by 2 - ; and add BUF_LINES - push hl ; --> lvl 1 - ex de, hl - call bufLineAddr - ex de, hl - pop hl ; <-- lvl 1 - call bufLineAddr - ; Both HL and DE are translated. Go! - ldir - ret - -; Insert string where DE points to memory scratchpad, then insert that line -; at index HL, offsetting all lines by 2 bytes. -bufInsertLine: - call bufIndexInBounds - jr nz, .append - push de ; --> lvl 1, scratchpad ptr - push hl ; --> lvl 2, insert index - ; The logic below is mostly copy-pasted from bufDelLines, but with a - ; LDDR logic (to avoid overwriting). I learned, with some pain involved, - ; that generalizing this code wasn't working very well. I don't repeat - ; the comments, refer to bufDelLines - ex de, hl ; line index now in DE - ld hl, (BUF_LINECNT) - scf \ ccf - sbc hl, de - ; mult by 2 and we're done - sla l \ rl h - push hl \ pop bc - ; From this point, we don't need our line index in DE any more because - ; LDDR will start from BUF_LINECNT-1 with count BC. We'll only need it - ; when it's time to insert the line in the space we make. - ld hl, (BUF_LINECNT) - call bufLineAddr - ; HL is pointing to *first byte* after last line. Our source needs to - ; be the second byte of the last line and our dest is the second byte - ; after the last line. - push hl \ pop de - dec hl ; second byte of last line - inc de ; second byte beyond last line - ; HL = BUF_LINECNT-1, DE = BUF_LINECNT, BC is set. We're good! - lddr -.set: - ; We still need to increase BUF_LINECNT - ld hl, (BUF_LINECNT) - inc hl - ld (BUF_LINECNT), hl - ; A space has been opened at line index HL. Let's fill it with our - ; inserted line. - pop hl ; <-- lvl 2, insert index - call bufLineAddr - pop de ; <-- lvl 1, scratchpad offset - ld (hl), e - inc hl - ld (hl), d - ret -.append: - ; nothing to move, just put the line there. Let's piggy-back on the end - ; of the regular routine by carefully pushing the right register in the - ; right place. - ; But before that, make sure that HL isn't too high. The only place we - ; can append to is at (BUF_LINECNT) - ld hl, (BUF_LINECNT) - push de ; --> lvl 1 - push hl ; --> lvl 2 - jr .set - -; copy string that HL points to to scratchpad and return its pointer in -; scratchpad, in HL. -bufScratchpadAdd: - push de - ld de, (BUF_PADEND) - push de ; --> lvl 1 - call strcpyM - inc de ; pad end is last char + 1 - ld (BUF_PADEND), de - pop hl ; <-- lvl 1 - pop de - ret - -; Sets Z according to whether the line index in HL is within bounds. -bufIndexInBounds: - push de - ld de, (BUF_LINECNT) - call cpHLDE - pop de - jr c, .withinBounds - ; out of bounds - jp unsetZ -.withinBounds: - cp a ; ensure Z - ret diff --git a/apps/ed/cmd.asm b/apps/ed/cmd.asm deleted file mode 100644 index 46e58c4..0000000 --- a/apps/ed/cmd.asm +++ /dev/null @@ -1,150 +0,0 @@ -; cmd - parse and interpret command -; -; *** Consts *** - -; address type - -.equ ABSOLUTE 0 -; handles +, - and ".". For +, easy. For -, addr is negative. For ., it's 0. -.equ RELATIVE 1 -.equ EOF 2 - -; *** Variables *** - -; An address is a one byte type and a two bytes line number (0-indexed) -.equ CMD_ADDR1 CMD_RAMSTART -.equ CMD_ADDR2 @+3 -.equ CMD_TYPE @+3 -.equ CMD_RAMEND @+1 - -; *** Code *** - -; Parse command line that HL points to and set unit's variables -; Sets Z on success, unset on error. -cmdParse: - ld a, (hl) - cp 'q' - jr z, .simpleCmd - cp 'w' - jr z, .simpleCmd - ld ix, CMD_ADDR1 - call .readAddr - ret nz - ; Before we check for the existence of a second addr, let's set that - ; second addr to the same value as the first. That's going to be its - ; value if we have to ",". - ld a, (ix) - ld (CMD_ADDR2), a - ld a, (ix+1) - ld (CMD_ADDR2+1), a - ld a, (ix+2) - ld (CMD_ADDR2+2), a - ld a, (hl) - cp ',' - jr nz, .noaddr2 - inc hl - ld ix, CMD_ADDR2 - call .readAddr - ret nz -.noaddr2: - ; We expect HL (rest of the cmdline) to be a null char or an accepted - ; cmd, otherwise it's garbage - ld a, (hl) - or a - jr z, .nullCmd - cp 'p' - jr z, .okCmd - cp 'd' - jr z, .okCmd - cp 'a' - jr z, .okCmd - cp 'i' - jr z, .okCmd - ; unsupported cmd - ret ; Z unset -.nullCmd: - ld a, 'p' -.okCmd: - ld (CMD_TYPE), a - ret ; Z already set - -.simpleCmd: - ; Z already set - ld (CMD_TYPE), a - ret - -; Parse the string at (HL) and sets its corresponding address in IX, properly -; considering implicit values (current address when nothing is specified). -; advances HL to the char next to the last parsed char. -; It handles "+" and "-" addresses such as "+3", "-2", "+", "-". -; Sets Z on success, unset on error. Line out of bounds isn't an error. Only -; overflows. -.readAddr: - ld a, (hl) - cp '+' - jr z, .plusOrMinus - cp '-' - jr z, .plusOrMinus - cp '.' - jr z, .dot - cp '$' - jr z, .eof - - ; inline parseDecimalDigit - add a, 0xff-'9' - sub 0xff-9 - - jr c, .notHandled - ; straight number - ld a, ABSOLUTE - ld (ix), a - call parseDecimal - ret nz - dec de ; from 1-based to 0-base - jr .end -.dot: - inc hl ; advance cmd cursor - ; the rest is the same as .notHandled -.notHandled: - ; something else. It's probably our command. Our addr is therefore "." - ld a, RELATIVE - ld (ix), a - xor a ; sets Z - ld (ix+1), a - ld (ix+2), a - ret -.eof: - inc hl ; advance cmd cursor - ld a, EOF - ld (ix), a - ret ; Z set during earlier CP -.plusOrMinus: - push af ; preserve that + or - - ld a, RELATIVE - ld (ix), a - inc hl ; advance cmd cursor - ld a, (hl) - ld de, 1 ; if .pmNoSuffix - - ; inline parseDecimalDigit - add a, 0xff-'9' - sub 0xff-9 - - jr c, .pmNoSuffix - call parseDecimal ; --> DE -.pmNoSuffix: - pop af ; bring back that +/- - cp '-' - jr nz, .end - ; we had a "-". Negate DE - push hl - ld hl, 0 - sbc hl, de - ex de, hl - pop hl -.end: - ; we still have to save DE in memory - ld (ix+1), e - ld (ix+2), d - cp a ; ensure Z - ret diff --git a/apps/ed/glue.asm b/apps/ed/glue.asm deleted file mode 100644 index d854d02..0000000 --- a/apps/ed/glue.asm +++ /dev/null @@ -1,43 +0,0 @@ -; *** Requirements *** -; _blkGetB -; _blkPutB -; _blkSeek -; _blkTell -; fsFindFN -; fsOpen -; fsGetB -; fsPutB -; fsSetSize -; printstr -; printcrlf -; stdioReadLine -; stdioPutC -; -.inc "user.h" - -; *** Overridable consts *** -; Maximum number of lines allowed in the buffer. -.equ ED_BUF_MAXLINES 0x800 -; Size of our scratchpad -.equ ED_BUF_PADMAXLEN 0x1000 - -; ****** - -.inc "err.h" -.inc "fs.h" -.inc "blkdev.h" - jp edMain - -.inc "core.asm" -.inc "lib/util.asm" -.inc "lib/parse.asm" -.inc "ed/util.asm" -.equ IO_RAMSTART USER_RAMSTART -.inc "ed/io.asm" -.equ BUF_RAMSTART IO_RAMEND -.inc "ed/buf.asm" -.equ CMD_RAMSTART BUF_RAMEND -.inc "ed/cmd.asm" -.equ ED_RAMSTART CMD_RAMEND -.inc "ed/main.asm" -USER_RAMSTART: diff --git a/apps/ed/io.asm b/apps/ed/io.asm deleted file mode 100644 index 0672244..0000000 --- a/apps/ed/io.asm +++ /dev/null @@ -1,93 +0,0 @@ -; io - handle ed's I/O - -; *** Consts *** -; -; Max length of a line -.equ IO_MAXLEN 0x7f - -; *** Variables *** -; Handle of the target file -.equ IO_FILE_HDL IO_RAMSTART -; block device targeting IO_FILE_HDL -.equ IO_BLK @+FS_HANDLE_SIZE -; Buffer for lines read from I/O. -.equ IO_LINE @+BLOCKDEV_SIZE -.equ IO_RAMEND @+IO_MAXLEN+1 ; +1 for null -; *** Code *** - -; Given a file name in (HL), open that file in (IO_FILE_HDL) and open a blkdev -; on it at (IO_BLK). -ioInit: - call fsFindFN - ret nz - ld ix, IO_FILE_HDL - call fsOpen - ld de, IO_BLK - ld hl, .blkdev - jp blkSet -.fsGetB: - ld ix, IO_FILE_HDL - jp fsGetB -.fsPutB: - ld ix, IO_FILE_HDL - jp fsPutB -.blkdev: - .dw .fsGetB, .fsPutB - -ioGetB: - push ix - ld ix, IO_BLK - call _blkGetB - pop ix - ret - -ioPutB: - push ix - ld ix, IO_BLK - call _blkPutB - pop ix - ret - -ioSeek: - push ix - ld ix, IO_BLK - call _blkSeek - pop ix - ret - -ioTell: - push ix - ld ix, IO_BLK - call _blkTell - pop ix - ret - -ioSetSize: - push ix - ld ix, IO_FILE_HDL - call fsSetSize - pop ix - ret - -; Write string (HL) in current file. Ends line with LF. -ioPutLine: - push hl -.loop: - ld a, (hl) - or a - jr z, .loopend ; null, we're finished - call ioPutB - jr nz, .error - inc hl - jr .loop -.loopend: - ; Wrote the whole line, write ending LF - ld a, 0x0a - call ioPutB - jr z, .end ; success - ; continue to error -.error: - call unsetZ -.end: - pop hl - ret diff --git a/apps/ed/main.asm b/apps/ed/main.asm deleted file mode 100644 index fbdef60..0000000 --- a/apps/ed/main.asm +++ /dev/null @@ -1,176 +0,0 @@ -; ed - line editor -; -; A text editor modeled after UNIX's ed, but simpler. The goal is to stay tight -; on resources and to avoid having to implement screen management code (that is, -; develop the machinery to have ncurses-like apps in Collapse OS). -; -; ed has a mechanism to avoid having to move a lot of memory around at each -; edit. Each line is an element in an doubly-linked list and each element point -; to an offset in the "scratchpad". The scratchpad starts with the file -; contents and every time we change or add a line, that line goes to the end of -; the scratch pad and linked lists are reorganized whenever lines are changed. -; Contents itself is always appended to the scratchpad. -; -; That's on a resourceful UNIX system. -; -; That doubly linked list on the z80 would use 7 bytes per line (prev, next, -; offset, len), which is a bit much. -; -; We sacrifice speed for memory usage by making that linked list into a simple -; array of pointers to line contents in scratchpad. This means that we -; don't have an easy access to line length and we have to move a lot of memory -; around whenever we add or delete lines. Hopefully, "LDIR" will be our friend -; here... -; -; *** Variables *** -; -.equ ED_CURLINE ED_RAMSTART -.equ ED_RAMEND @+2 - -edMain: - ; because ed only takes a single string arg, we can use HL directly - call ioInit - ret nz - ; diverge from UNIX: start at first line - ld hl, 0 - ld (ED_CURLINE), hl - - call bufInit - -.mainLoop: - ld a, ':' - call stdioPutC - call stdioReadLine ; --> HL - ; Now, process line. - call printcrlf - call cmdParse - jp nz, .error - ld a, (CMD_TYPE) - cp 'q' - jr z, .doQ - cp 'w' - jr z, .doW - ; The rest of the commands need an address - call edReadAddrs - jr nz, .error - ld a, (CMD_TYPE) - cp 'i' - jr z, .doI - ; The rest of the commands don't allow addr == cnt - push hl ; --> lvl 1 - ld hl, (BUF_LINECNT) - call cpHLDE - pop hl ; <-- lvl 1 - jr z, .error - ld a, (CMD_TYPE) - cp 'd' - jr z, .doD - cp 'a' - jr z, .doA - jr .doP - -.doQ: - xor a - ret - -.doW: - ld a, 3 ; seek beginning - call ioSeek - ld de, 0 ; cur line -.wLoop: - push de \ pop hl - call bufGetLine ; --> buffer in (HL) - jr nz, .wEnd - call ioPutLine - jr nz, .error - inc de - jr .wLoop -.wEnd: - ; Set new file size - call ioTell - call ioSetSize - ; for now, writing implies quitting - ; TODO: reload buffer - xor a - ret -.doD: - ld (ED_CURLINE), de - ; bufDelLines expects an exclusive upper bound, which is why we inc DE. - inc de - call bufDelLines - jr .mainLoop -.doA: - inc de -.doI: - call stdioReadLine ; --> HL - call bufScratchpadAdd ; --> HL - ; insert index in DE, line offset in HL. We want the opposite. - ex de, hl - ld (ED_CURLINE), hl - call bufInsertLine - call printcrlf - jr .mainLoop - -.doP: - push hl - call bufGetLine - jr nz, .error - call printstr - call printcrlf - pop hl - call cpHLDE - jr z, .doPEnd - inc hl - jr .doP -.doPEnd: - ld (ED_CURLINE), hl - jp .mainLoop -.error: - ld a, '?' - call stdioPutC - call printcrlf - jp .mainLoop - - -; Transform an address "cmd" in IX into an absolute address in HL. -edResolveAddr: - ld a, (ix) - cp RELATIVE - jr z, .relative - cp EOF - jr z, .eof - ; absolute - ld l, (ix+1) - ld h, (ix+2) - ret -.relative: - ld hl, (ED_CURLINE) - push de - ld e, (ix+1) - ld d, (ix+2) - add hl, de - pop de - ret -.eof: - ld hl, (BUF_LINECNT) - dec hl - ret - -; Read absolute addr1 in HL and addr2 in DE. Also, check bounds and set Z if -; both addresses are within bounds, unset if not. -edReadAddrs: - ld ix, CMD_ADDR2 - call edResolveAddr - ld de, (BUF_LINECNT) - ex de, hl ; HL: cnt DE: addr2 - call cpHLDE - jp c, unsetZ ; HL (cnt) < DE (addr2). no good - ld ix, CMD_ADDR1 - call edResolveAddr - ex de, hl ; HL: addr2, DE: addr1 - call cpHLDE - jp c, unsetZ ; HL (addr2) < DE (addr1). no good - ex de, hl ; HL: addr1, DE: addr2 - cp a ; ensure Z - ret - diff --git a/apps/ed/util.asm b/apps/ed/util.asm deleted file mode 100644 index b23b658..0000000 --- a/apps/ed/util.asm +++ /dev/null @@ -1,8 +0,0 @@ -; Compare HL with DE and sets Z and C in the same way as a regular cp X where -; HL is A and DE is X. -cpHLDE: - push hl - or a ;reset carry flag - sbc hl, de ;There is no 'sub hl, de', so we must use sbc - pop hl - ret diff --git a/apps/lib/README.md b/apps/lib/README.md deleted file mode 100644 index 1791b33..0000000 --- a/apps/lib/README.md +++ /dev/null @@ -1 +0,0 @@ -Common code used by more than one app, but not by the kernel. diff --git a/apps/lib/ari.asm b/apps/lib/ari.asm deleted file mode 100644 index 8f40838..0000000 --- a/apps/lib/ari.asm +++ /dev/null @@ -1,44 +0,0 @@ -; Borrowed from Tasty Basic by Dimitri Theulings (GPL). -; Divide HL by DE, placing the result in BC and the remainder in HL. -divide: - push hl ; --> lvl 1 - ld l, h ; divide h by de - ld h, 0 - call .dv1 - ld b, c ; save result in b - ld a, l ; (remainder + l) / de - pop hl ; <-- lvl 1 - ld h, a -.dv1: - ld c, 0xff ; result in c -.dv2: - inc c ; dumb routine - call .subde ; divide using subtract and count - jr nc, .dv2 - add hl, de - ret -.subde: - ld a, l - sub e ; subtract de from hl - ld l, a - ld a, h - sbc a, d - ld h, a - ret - -; DE * BC -> DE (high) and HL (low) -multDEBC: - ld hl, 0 - ld a, 0x10 -.loop: - add hl, hl - rl e - rl d - jr nc, .noinc - add hl, bc - jr nc, .noinc - inc de -.noinc: - dec a - jr nz, .loop - ret diff --git a/apps/lib/expr.asm b/apps/lib/expr.asm deleted file mode 100644 index df25300..0000000 --- a/apps/lib/expr.asm +++ /dev/null @@ -1,267 +0,0 @@ -; *** Requirements *** -; ari -; -; *** Defines *** -; -; EXPR_PARSE: routine to call to parse literals or symbols that are part of -; the expression. Routine's signature: -; String in (HL), returns its parsed value to DE. Z for success. -; HL is advanced to the character following the last successfully -; read char. -; -; *** Code *** -; -; Parse expression in string at (HL) and returns the result in DE. -; This routine needs to be able to mutate (HL), but it takes care of restoring -; the string to its original value before returning. -; Sets Z on success, unset on error. -parseExpr: - push iy - push ix - push hl - call _parseAddSubst - pop hl - pop ix - pop iy - ret - -; *** Op signature *** -; The signature of "operators routines" (.plus, .mult, etc) below is this: -; Combine HL and DE with an operator (+, -, *, etc) and put the result in DE. -; Destroys HL and A. Never fails. Yes, that's a problem for division by zero. -; Don't divide by zero. All other registers are protected. - -; Given a running result in DE, a rest-of-expression in (HL), a parse routine -; in IY and an apply "operator routine" in IX, (HL/DE --> DE) -; With that, parse the rest of (HL) and apply the operation on it, then place -; HL at the end of the parsed string, with A containing the last char of it, -; which can be either an operator or a null char. -; Z for success. -; -_parseApply: - push de ; --> lvl 1, left result - push ix ; --> lvl 2, routine to apply - inc hl ; after op char - call callIY ; --> DE - pop ix ; <-- lvl 2, routine to apply - ; Here we do some stack kung fu. We have, in HL, a string pointer we - ; want to keep. We have, in (SP), our left result we want to use. - ex (sp), hl ; <-> lvl 1 - jr nz, .end - push af ; --> lvl 2, save ending operator - call callIX - pop af ; <-- lvl 2, restore operator. -.end: - pop hl ; <-- lvl 1, restore str pointer - ret - -; Unless there's an error, this routine completely resolves any valid expression -; from (HL) and puts the result in DE. -; Destroys HL -; Z for success. -_parseAddSubst: - call _parseMultDiv - ret nz -.loop: - ; do we have an operator? - or a - ret z ; null char, we're done - ; We have an operator. Resolve the rest of the expr then apply it. - ld ix, .plus - cp '+' - jr z, .found - ld ix, .minus - cp '-' - ret nz ; unknown char, error -.found: - ld iy, _parseMultDiv - call _parseApply - ret nz - jr .loop -.plus: - add hl, de - ex de, hl - ret -.minus: - or a ; clear carry - sbc hl, de - ex de, hl - ret - -; Parse (HL) as far as it can, that is, resolving expressions at its level or -; lower (anything but + and -). -; A is set to the last op it encountered. Unless there's an error, this can only -; be +, - or null. Null if we're done parsing, + and - if there's still work to -; do. -; (HL) points to last op encountered. -; DE is set to the numerical value of everything that was parsed left of (HL). -_parseMultDiv: - call _parseBitShift - ret nz -.loop: - ; do we have an operator? - or a - ret z ; null char, we're done - ; We have an operator. Resolve the rest of the expr then apply it. - ld ix, .mult - cp '*' - jr z, .found - ld ix, .div - cp '/' - jr z, .found - ld ix, .mod - cp '%' - jr z, .found - ; might not be an error, return success - cp a - ret -.found: - ld iy, _parseBitShift - call _parseApply - ret nz - jr .loop - -.mult: - push bc ; --> lvl 1 - ld b, h - ld c, l - call multDEBC ; --> HL - pop bc ; <-- lvl 1 - ex de, hl - ret - -.div: - ; divide takes HL/DE - ld a, l - push bc ; --> lvl 1 - call divide - ld e, c - ld d, b - pop bc ; <-- lvl 1 - ret - -.mod: - call .div - ex de, hl - ret - -; Same as _parseMultDiv, but a layer lower. -_parseBitShift: - call _parseNumber - ret nz -.loop: - ; do we have an operator? - or a - ret z ; null char, we're done - ; We have an operator. Resolve the rest of the expr then apply it. - ld ix, .and - cp '&' - jr z, .found - ld ix, .or - cp 0x7c ; '|' - jr z, .found - ld ix, .xor - cp '^' - jr z, .found - ld ix, .rshift - cp '}' - jr z, .found - ld ix, .lshift - cp '{' - jr z, .found - ; might not be an error, return success - cp a - ret -.found: - ld iy, _parseNumber - call _parseApply - ret nz - jr .loop - -.and: - ld a, h - and d - ld d, a - ld a, l - and e - ld e, a - ret -.or: - ld a, h - or d - ld d, a - ld a, l - or e - ld e, a - ret - -.xor: - ld a, h - xor d - ld d, a - ld a, l - xor e - ld e, a - ret - -.rshift: - ld a, e - and 0xf - ret z - push bc ; --> lvl 1 - ld b, a -.rshiftLoop: - srl h - rr l - djnz .rshiftLoop - ex de, hl - pop bc ; <-- lvl 1 - ret - -.lshift: - ld a, e - and 0xf - ret z - push bc ; --> lvl 1 - ld b, a -.lshiftLoop: - sla l - rl h - djnz .lshiftLoop - ex de, hl - pop bc ; <-- lvl 1 - ret - -; Parse first number of expression at (HL). A valid number is anything that can -; be parsed by EXPR_PARSE and is followed either by a null char or by any of the -; operator chars. This routines takes care of replacing an operator char with -; the null char before calling EXPR_PARSE and then replace the operator back -; afterwards. -; HL is moved to the char following the number having been parsed. -; DE contains the numerical result. -; A contains the operator char following the number (or null). Only on success. -; Z for success. -_parseNumber: - ; Special case 1: number starts with '-' - ld a, (hl) - cp '-' - jr nz, .skip1 - ; We have a negative number. Parse normally, then subst from zero - inc hl - call _parseNumber - push hl ; --> lvl 1 - ex af, af' ; preserve flags - or a ; clear carry - ld hl, 0 - sbc hl, de - ex de, hl - ex af, af' ; restore flags - pop hl ; <-- lvl 1 - ret -.skip1: - ; End of special case 1 - call EXPR_PARSE ; --> DE - ret nz - ; Check if (HL) points to null or op - ld a, (hl) - ret diff --git a/apps/lib/fmt.asm b/apps/lib/fmt.asm deleted file mode 100644 index 6909712..0000000 --- a/apps/lib/fmt.asm +++ /dev/null @@ -1,115 +0,0 @@ -; *** Requirements *** -; stdioPutC -; divide -; - -; Same as fmtDecimal, but DE is considered a signed number -fmtDecimalS: - bit 7, d - jr z, fmtDecimal ; unset, not negative - ; Invert DE. spit '-', unset bit, then call fmtDecimal - push de - ld a, '-' - ld (hl), a - inc hl - ld a, d - cpl - ld d, a - ld a, e - cpl - ld e, a - inc de - call fmtDecimal - dec hl - pop de - ret - -; Format the number in DE into the string at (HL) in a decimal form. -; Null-terminated. DE is considered an unsigned number. -fmtDecimal: - push ix - push hl - push de - push af - - push hl \ pop ix - ex de, hl ; orig number now in HL - ld e, 0 -.loop1: - call .div10 - push hl ; push remainder. --> lvl E - inc e - ld a, b ; result 0? - or c - push bc \ pop hl - jr nz, .loop1 ; not zero, continue - ; We now have C digits to print in the stack. - ; Spit them! - push ix \ pop hl ; restore orig HL. - ld b, e -.loop2: - pop de ; <-- lvl E - ld a, '0' - add a, e - ld (hl), a - inc hl - djnz .loop2 - - ; null terminate - xor a - ld (hl), a - pop af - pop de - pop hl - pop ix - ret -.div10: - push de - ld de, 0x000a - call divide - pop de - ret - -; Format the lower nibble of A into a hex char and stores the result in A. -fmtHex: - ; The idea here is that there's 7 characters between '9' and 'A' - ; in the ASCII table, and so we add 7 if the digit is >9. - ; daa is designed for using Binary Coded Decimal format, where each - ; nibble represents a single base 10 digit. If a nibble has a value >9, - ; it adds 6 to that nibble, carrying to the next nibble and bringing the - ; value back between 0-9. This gives us 6 of that 7 we needed to add, so - ; then we just condtionally set the carry and add that carry, along with - ; a number that maps 0 to '0'. We also need the upper nibble to be a - ; set value, and have the N, C and H flags clear. - or 0xf0 - daa ; now a =0x50 + the original value + 0x06 if >= 0xfa - add a, 0xa0 ; cause a carry for the values that were >=0x0a - adc a, 0x40 - ret - -; Print the hex char in A as a pair of hex digits. -printHex: - push af - - ; let's start with the leftmost char - rra \ rra \ rra \ rra - call fmtHex - call stdioPutC - - ; and now with the rightmost - pop af \ push af - call fmtHex - call stdioPutC - - pop af - ret - -; Print the hex pair in HL -printHexPair: - push af - ld a, h - call printHex - ld a, l - call printHex - pop af - ret diff --git a/apps/lib/parse.asm b/apps/lib/parse.asm deleted file mode 100644 index b727f0d..0000000 --- a/apps/lib/parse.asm +++ /dev/null @@ -1,238 +0,0 @@ -; *** Requirements *** -; lib/util -; *** Code *** - -; Parse the hex char at A and extract it's 0-15 numerical value. Put the result -; in A. -; -; On success, the carry flag is reset. On error, it is set. -parseHex: - ; First, let's see if we have an easy 0-9 case - - add a, 0xc6 ; maps '0'-'9' onto 0xf6-0xff - sub 0xf6 ; maps to 0-9 and carries if not a digit - ret nc - - and 0xdf ; converts lowercase to uppercase - add a, 0xe9 ; map 0x11-x017 onto 0xFA - 0xFF - sub 0xfa ; map onto 0-6 - ret c - ; we have an A-F digit - add a, 10 ; C is clear, map back to 0xA-0xF - ret - -; Parse string at (HL) as a decimal value and return value in DE. -; Reads as many digits as it can and stop when: -; 1 - A non-digit character is read -; 2 - The number overflows from 16-bit -; HL is advanced to the character following the last successfully read char. -; Error conditions are: -; 1 - There wasn't at least one character that could be read. -; 2 - Overflow. -; Sets Z on success, unset on error. - -parseDecimal: - ; First char is special: it has to succeed. - ld a, (hl) - ; Parse the decimal char at A and extract it's 0-9 numerical value. Put the - ; result in A. - ; On success, the carry flag is reset. On error, it is set. - add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff - sub 0xff-9 ; maps to 0-9 and carries if not a digit - ret c ; Error. If it's C, it's also going to be NZ - ; During this routine, we switch between HL and its shadow. On one side, - ; we have HL the string pointer, and on the other side, we have HL the - ; numerical result. We also use EXX to preserve BC, saving us a push. -parseDecimalSkip: ; enter here to skip parsing the first digit - exx ; HL as a result - ld h, 0 - ld l, a ; load first digit in without multiplying - -.loop: - exx ; HL as a string pointer - inc hl - ld a, (hl) - exx ; HL as a numerical result - - ; same as other above - add a, 0xff-'9' - sub 0xff-9 - jr c, .end - - ld b, a ; we can now use a for overflow checking - add hl, hl ; x2 - sbc a, a ; a=0 if no overflow, a=0xFF otherwise - ld d, h - ld e, l ; de is x2 - add hl, hl ; x4 - rla - add hl, hl ; x8 - rla - add hl, de ; x10 - rla - ld d, a ; a is zero unless there's an overflow - ld e, b - add hl, de - adc a, a ; same as rla except affects Z - ; Did we oveflow? - jr z, .loop ; No? continue - ; error, NZ already set - exx ; HL is now string pointer, restore BC - ; HL points to the char following the last success. - ret - -.end: - push hl ; --> lvl 1, result - exx ; HL as a string pointer, restore BC - pop de ; <-- lvl 1, result - cp a ; ensure Z - ret - -; Call parseDecimal and then check that HL points to a whitespace or a null. -parseDecimalC: - call parseDecimal - ret nz - ld a, (hl) - or a - ret z ; null? we're happy - jp isWS - -; Parse string at (HL) as a hexadecimal value without the "0x" prefix and -; return value in DE. -; HL is advanced to the character following the last successfully read char. -; Sets Z on success. -parseHexadecimal: - ld a, (hl) - call parseHex ; before "ret c" is "sub 0xfa" in parseHex - ; so carry implies not zero - ret c ; we need at least one char - push bc - ld de, 0 - ld b, d - ld c, d - -; The idea here is that the 4 hex digits of the result can be represented "bdce", -; where each register holds a single digit. Then the result is simply -; e = (c << 4) | e, d = (b << 4) | d -; However, the actual string may be of any length, so when loading in the most -; significant digit, we don't know which digit of the result it actually represents -; To solve this, after a digit is loaded into a (and is checked for validity), -; all digits are moved along, with e taking the latest digit. -.loop: - dec b - inc b ; b should be 0, else we've overflowed - jr nz, .end ; Z already unset if overflow - ld b, d - ld d, c - ld c, e - ld e, a - inc hl - ld a, (hl) - call parseHex - jr nc, .loop - ld a, b - add a, a \ add a, a \ add a, a \ add a, a - or d - ld d, a - - ld a, c - add a, a \ add a, a \ add a, a \ add a, a - or e - ld e, a - xor a ; ensure z - -.end: - pop bc - ret - - -; Parse string at (HL) as a binary value (010101) without the "0b" prefix and -; return value in E. D is always zero. -; HL is advanced to the character following the last successfully read char. -; Sets Z on success. -parseBinaryLiteral: - ld de, 0 -.loop: - ld a, (hl) - add a, 0xff-'1' - sub 0xff-1 - jr c, .end - rlc e ; sets carry if overflow, and affects Z - ret c ; Z unset if carry set, since bit 0 of e must be set - add a, e - ld e, a - inc hl - jr .loop -.end: - ; HL is properly set - xor a ; ensure Z - ret - -; Parses the string at (HL) and returns the 16-bit value in DE. The string -; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char -; literal ('X'). -; HL is advanced to the character following the last successfully read char. -; -; As soon as the number doesn't fit 16-bit any more, parsing stops and the -; number is invalid. If the number is valid, Z is set, otherwise, unset. -parseLiteral: - ld de, 0 ; pre-fill - ld a, (hl) - cp 0x27 ; apostrophe - jr z, .char - - ; inline parseDecimalDigit - add a, 0xc6 ; maps '0'-'9' onto 0xf6-0xff - sub 0xf6 ; maps to 0-9 and carries if not a digit - ret c - ; a already parsed so skip first few instructions of parseDecimal - jp nz, parseDecimalSkip - ; maybe hex, maybe binary - inc hl - ld a, (hl) - inc hl ; already place it for hex or bin - cp 'x' - jr z, parseHexadecimal - cp 'b' - jr z, parseBinaryLiteral - ; nope, just a regular decimal - dec hl \ dec hl - jp parseDecimal - -; Parse string at (HL) and, if it is a char literal, sets Z and return -; corresponding value in E. D is always zero. -; HL is advanced to the character following the last successfully read char. -; -; A valid char literal starts with ', ends with ' and has one character in the -; middle. No escape sequence are accepted, but ''' will return the apostrophe -; character. -.char: - inc hl - ld e, (hl) ; our result - inc hl - cp (hl) - ; advance HL and return if good char - inc hl - ret z - - ; Z unset and there's an error - ; In all error conditions, HL is advanced by 3. Rewind. - dec hl \ dec hl \ dec hl - ; NZ already set - ret - - -; Returns whether A is a literal prefix, that is, a digit or an apostrophe. -isLiteralPrefix: - cp 0x27 ; apostrophe - ret z - ; continue to isDigit - -; Returns whether A is a digit -isDigit: - cp '0' ; carry implies not zero for cp - ret c - cp '9' ; zero unset for a > '9', but set for a='9' - ret nc - cp a ; ensure Z - ret diff --git a/apps/lib/util.asm b/apps/lib/util.asm deleted file mode 100644 index 715c072..0000000 --- a/apps/lib/util.asm +++ /dev/null @@ -1,114 +0,0 @@ -; Sets Z is A is ' ' or '\t' (whitespace) -isWS: - cp ' ' - ret z - cp 0x09 - ret - -; Advance HL to next WS. -; Set Z if WS found, unset if end-of-string. -toWS: - ld a, (hl) - call isWS - ret z - cp 0x01 ; if a is null, carries and unsets z - ret c - inc hl - jr toWS - -; Consume following whitespaces in HL until a non-WS is hit. -; Set Z if non-WS found, unset if end-of-string. -rdWS: - ld a, (hl) - cp 0x01 ; if a is null, carries and unsets z - ret c - call isWS - jr nz, .ok - inc hl - jr rdWS -.ok: - cp a ; ensure Z - ret - -; Copy string from (HL) in (DE), that is, copy bytes until a null char is -; encountered. The null char is also copied. -; HL and DE point to the char right after the null char. -strcpyM: - ld a, (hl) - ld (de), a - inc hl - inc de - or a - jr nz, strcpyM - ret - -; Like strcpyM, but preserve HL and DE -strcpy: - push hl - push de - call strcpyM - pop de - pop hl - ret - -; Compares strings pointed to by HL and DE until one of them hits its null char. -; If equal, Z is set. If not equal, Z is reset. C is set if HL > DE -strcmp: - push hl - push de - -.loop: - ld a, (de) - cp (hl) - jr nz, .end ; not equal? break early. NZ is carried out - ; to the caller - or a ; If our chars are null, stop the cmp - inc hl - inc de - jr nz, .loop ; Z is carried through - -.end: - pop de - pop hl - ; Because we don't call anything else than CP that modify the Z flag, - ; our Z value will be that of the last cp (reset if we broke the loop - ; early, set otherwise) - ret - -; Given a string at (HL), move HL until it points to the end of that string. -strskip: - push bc - ex af, af' - xor a ; look for null char - ld b, a - ld c, a - cpir ; advances HL regardless of comparison, so goes one too far - dec hl - ex af, af' - pop bc - ret - -; Returns length of string at (HL) in A. -; Doesn't include null termination. -strlen: - push bc - xor a ; look for null char - ld b, a - ld c, a - cpir ; advances HL to the char after the null -.found: - ; How many char do we have? We have strlen=(NEG BC)-1, since BC started - ; at 0 and decreased at each CPIR loop. In this routine, - ; we stay in the 8-bit realm, so C only. - add hl, bc - sub c - dec a - pop bc - ret - -; make Z the opposite of what it is now -toggleZ: - jp z, unsetZ - cp a - ret - diff --git a/apps/memt/glue.asm b/apps/memt/glue.asm deleted file mode 100644 index f3389f3..0000000 --- a/apps/memt/glue.asm +++ /dev/null @@ -1,21 +0,0 @@ -; memt -; -; Write all possible values in all possible addresses that follow the end of -; this program. That means we don't test all available RAM, but well, still -; better than nothing... -; -; If there's an error, prints out where. -; -; *** Requirements *** -; printstr -; stdioPutC -; -; *** Includes *** - -.inc "user.h" -jp memtMain - -.inc "lib/ari.asm" -.inc "lib/fmt.asm" -.inc "memt/main.asm" -USER_RAMSTART: diff --git a/apps/memt/main.asm b/apps/memt/main.asm deleted file mode 100644 index 62bd30c..0000000 --- a/apps/memt/main.asm +++ /dev/null @@ -1,33 +0,0 @@ -memtMain: - ld de, memtEnd -.loop: - ld b, 0 -.iloop: - ld a, b - ld (de), a - ld a, (de) - cp b - jr nz, .notMatching - djnz .iloop - inc de - xor a - cp d - jr nz, .loop - cp e - jr nz, .loop - ; we rolled over 0xffff, stop - ld hl, .sOk - xor a - jp printstr ; returns -.notMatching: - ld hl, .sNotMatching - call printstr - ex de, hl - ld a, 1 - jp printHexPair ; returns -.sNotMatching: - .db "Not matching at pos ", 0xd, 0xa, 0 -.sOk: - .db "OK", 0xd, 0xa, 0 -memtEnd: - diff --git a/apps/sdct/README.md b/apps/sdct/README.md deleted file mode 100644 index 22d221b..0000000 --- a/apps/sdct/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# sdct - test SD Card - -This program stress-tests a SD card by repeatedly reading and writing to it and -verify that data stays the same. diff --git a/apps/sdct/glue.asm b/apps/sdct/glue.asm deleted file mode 100644 index 72227d9..0000000 --- a/apps/sdct/glue.asm +++ /dev/null @@ -1,29 +0,0 @@ -; sdct -; -; We want to test reading and writing random data in random sequences of -; sectors. Collapse OS doesn't have a random number generator, so we'll simply -; rely on initial SRAM value, which tend is random enough for our purpose. -; -; How it works is simple. From its designated RAMSTART, it calls PutB until it -; reaches the end of RAM (0xffff). Then, it starts over and this time it reads -; every byte and compares. -; -; If there's an error, prints out where. -; -; *** Requirements *** -; sdcPutB -; sdcGetB -; printstr -; stdioPutC -; -; *** Includes *** - -.inc "user.h" -.equ SDCT_RAMSTART USER_RAMSTART - -jp sdctMain - -.inc "lib/ari.asm" -.inc "lib/fmt.asm" -.inc "sdct/main.asm" -USER_RAMSTART: diff --git a/apps/sdct/main.asm b/apps/sdct/main.asm deleted file mode 100644 index b773c26..0000000 --- a/apps/sdct/main.asm +++ /dev/null @@ -1,72 +0,0 @@ -sdctMain: - ld hl, .sWriting - call printstr - ld hl, 0 - ld de, SDCT_RAMSTART -.wLoop: - ld a, (de) - ; To avoid overwriting important data and to test the 24-bit addressing, - ; we set DE to 12 instead of zero - push de ; <| - ld de, 12 ; | - call sdcPutB ; | - pop de ; <| - jr nz, .error - inc hl - inc de - ; Stop looping if DE == 0 - xor a - cp e - jr nz, .wLoop - ; print some kind of progress - call printHexPair - cp d - jr nz, .wLoop - ; Finished writing - ld hl, .sReading - call printstr - ld hl, 0 - ld de, SDCT_RAMSTART -.rLoop: - push de ; <| - ld de, 12 ; | - call sdcGetB ; | - pop de ; <| - jr nz, .error - ex de, hl - cp (hl) - ex de, hl - jr nz, .notMatching - inc hl - inc de - ; Stop looping if DE == 0 - xor a - cp d - jr nz, .rLoop - cp e - jr nz, .rLoop - ; Finished checking - xor a - ld hl, .sOk - jp printstr ; returns -.notMatching: - ; error position is in HL, let's preserve it - ex de, hl - ld hl, .sNotMatching - call printstr - ex de, hl - jp printHexPair ; returns -.error: - ld hl, .sErr - jp printstr ; returns - -.sWriting: - .db "Writing", 0xd, 0xa, 0 -.sReading: - .db "Reading", 0xd, 0xa, 0 -.sNotMatching: - .db "Not matching at pos ", 0xd, 0xa, 0 -.sErr: - .db "Error", 0xd, 0xa, 0 -.sOk: - .db "OK", 0xd, 0xa, 0 diff --git a/apps/zasm/README.md b/apps/zasm/README.md deleted file mode 100644 index 644a667..0000000 --- a/apps/zasm/README.md +++ /dev/null @@ -1,200 +0,0 @@ -# z80 assembler - -This is probably the most critical part of the Collapse OS project because it -ensures its self-reproduction. - -## Invocation - -`zasm` is invoked with 2 mandatory arguments and an optional one. The mandatory -arguments are input blockdev id and output blockdev id. For example, `zasm 0 1` -reads source code from blockdev 0, assembles it and spit the result in blockdev -1. - -Input blockdev needs to be seek-able, output blockdev doesn't need to (zasm -writes in one pass, sequentially. - -The 3rd argument, optional, is the initial `.org` value. It's the high byte of -the value. For example, `zasm 0 1 4f` assembles source in blockdev 0 as if it -started with the line `.org 0x4f00`. This also means that the initial value of -the `@` symbol is `0x4f00`. - -## Running on a "modern" machine - -To be able to develop zasm efficiently, [libz80][libz80] is used to run zasm -on a modern machine. The code lives in `emul` and ran be built with `make`, -provided that you have a copy libz80 living in `emul/libz80`. - -The resulting `zasm` binary takes asm code in stdin and spits binary in stdout. - -## Literals - -See "Number literals" in `apps/README.md`. - -On top of common literal logic, zasm also has string literals. It's a chain of -characters surrounded by double quotes. Example: `"foo"`. This literal can only -be used in the `.db` directive and is equivalent to each character being -single-quoted and separated by commas (`'f', 'o', 'o'`). No null char is -inserted in the resulting value (unlike what C does). - -## Labels - -Lines starting with a name followed `:` are labeled. When that happens, the -name of that label is associated with the binary offset of the following -instruction. - -For example, a label placed at the beginning of the file is associated with -offset 0. If placed right after a first instruction that is 2 bytes wide, then -the label is going to be bound to 2. - -Those labels can then be referenced wherever a constant is expected. They can -also be referenced where a relative reference is expected (`jr` and `djnz`). - -Labels can be forward-referenced, that is, you can reference a label that is -defined later in the source file or in an included source file. - -Labels starting with a dot (`.`) are local labels: they belong only to the -namespace of the current "global label" (any label that isn't local). Local -namespace is wiped whenever a global label is encountered. - -Local labels allows reuse of common mnemonics and make the assembler use less -memory. - -Global labels are all evaluated during the first pass, which makes possible to -forward-reference them. Local labels are evaluated during the second pass, but -we can still forward-reference them through a "first-pass-redux" hack. - -Labels can be alone on their line, but can also be "inlined", that is, directly -followed by an instruction. - -## Constants - -The `.equ` directive declares a constant. That constant's argument is an -expression that is evaluated right at parse-time. - -Constants are evaluated during the second pass, which means that they can -forward-reference labels. - -However, they *cannot* forward-reference other constants. - -When defining a constant, if the symbol specified has already been defined, no -error occur and the first value defined stays intact. This allows for "user -override" of programs. - -It's also important to note that constants always override labels, regardless -of declaration order. - -## Expressions - -See "Expressions" in `apps/README.md`. - -## The Program Counter - -The `$` is a special symbol that can be placed in any expression and evaluated -as the current output offset. That is, it's the value that a label would have if -it was placed there. - -## The Last Value - -Whenever a `.equ` directive is evaluated, its resulting value is saved in a -special "last value" register that can then be used in any expression. This -last value is referenced with the `@` special symbol. This is very useful for -variable definitions and for jump tables. - -Note that `.org` also affect the last value. - -## Includes - -The `.inc` directive is special. It takes a string literal as an argument and -opens, in the currently active filesystem, the file with the specified name. - -It then proceeds to parse that file as if its content had been copy/pasted in -the includer file, that is: global labels are kept and can be referenced -elsewhere. Constants too. An exception is local labels: a local namespace always -ends at the end of an included file. - -There an important limitation with includes: only one level of includes is -allowed. An included file cannot have an `.inc` directive. - -## Directives - -**.db**: Write bytes specified by the directive directly in the resulting - binary. Each byte is separated by a comma. Example: `.db 0x42, foo` - -**.dw**: Same as `.db`, but outputs words. Example: `.dw label1, label2` - -**.equ**: Binds a symbol named after the first parameter to the value of the - expression written as the second parameter. Example: - `.equ foo 0x42+'A'`. See "Constants" above. - -**.fill**: Outputs the number of null bytes specified by its argument, an - expression. Often used with `$` to fill our binary up to a certain - offset. For example, if we want to place an instruction exactly at - byte 0x38, we would precede it with `.fill 0x38-$`. - -The maximum value possible for `.fill` is `0xd000`. We do this to -avoid "overshoot" errors, that is, error where `$` is greater than -the offset you're trying to reach in an expression like `.fill X-$` -(such an expression overflows to `0xffff`). - -**.org**: Sets the Program Counter to the value of the argument, an expression. - For example, a label being defined right after a `.org 0x400`, would - have a value of `0x400`. Does not do any filling. You have to do that - explicitly with `.fill`, if needed. Often used to assemble binaries - designed to run at offsets other than zero (userland). - -**.out**: Outputs the value of the expression supplied as an argument to - `ZASM_DEBUG_PORT`. The value is always interpreted as a word, so - there's always two `out` instruction executed per directive. High byte - is sent before low byte. Useful or debugging, quickly figuring our - RAM constants, etc. The value is only outputted during the second - pass. - -**.inc**: Takes a string literal as an argument. Open the file name specified - in the argument in the currently active filesystem, parse that file - and output its binary content as is the code has been in the includer - file. - -**.bin**: Takes a string literal as an argument. Open the file name specified - in the argument in the currently active filesystem and outputs its - contents directly. - -## Undocumented instructions - -`zasm` doesn't support undocumented instructions such as the ones that involve -using `IX` and `IY` as 8-bit registers. We used to support them, but because -this makes our code incompatible with Z80-compatible CPUs such as the Z180, we -prefer to avoid these in our code. - -## AVR assembler - -`zasm` can be configured, at compile time, to be a AVR assembler instead of a -z80 assembler. Directives, literals, symbols, they're all the same, it's just -instructions and their arguments that change. - -Instructions and their arguments have a ayntax that is similar to other AVR -assemblers: registers are referred to as `rXX`, mnemonics are the same, -arguments are separated by commas. - -To assemble an AVR assembler, use the `gluea.asm` file instead of the regular -one. - -Note about AVR and PC: In most assemblers, arithmetics for instructions -addresses have words (two bytes) as their basic unit because AVR instructions -are either 16bit in length or 32bit in length. All addresses constants in -upcodes are in words. However, in zasm's core logic, PC is in bytes (because z80 -upcodes can be 1 byte). - -The AVR assembler, of course, correctly translates byte PCs to words when -writing upcodes, however, when you write your expressions, you need to remember -to treat with bytes. For example, in a traditional AVR assembler, jumping to -the instruction after the "foo" label would be "rjmp foo+1". In zasm, it's -"rjmp foo+2". If your expression results in an odd number, the low bit of your -number will be ignored. - -Limitations: - -* `CALL` and `JMP` only support 16-bit numbers, not 22-bit ones. -* `BRLO` and `BRSH` are not there. Use `BRCS` and `BRCC` instead. -* No `high()` and `low()`. Use `&0xff` and `}8`. - -[libz80]: https://github.com/ggambetta/libz80 diff --git a/apps/zasm/avr.asm b/apps/zasm/avr.asm deleted file mode 100644 index 326a4a3..0000000 --- a/apps/zasm/avr.asm +++ /dev/null @@ -1,846 +0,0 @@ -; Same thing as instr.asm, but for AVR instructions - -; *** Instructions table *** - -; List of mnemonic names separated by a null terminator. Their index in the -; list is their ID. Unlike in zasm, not all mnemonics have constant associated -; to it because it's generally not needed. This list is grouped by argument -; categories, and then alphabetically. Categories are ordered so that the 8bit -; opcodes come first, then the 16bit ones. 0xff ends the chain -instrNames: -; Branching instructions. They are all shortcuts to BRBC/BRBS. These are not in -; alphabetical order, but rather in "bit order". All "bit set" instructions -; first (10th bit clear), then all "bit clear" ones (10th bit set). Inside this -; order, they're then in "sss" order (bit number alias for BRBC/BRBS). -.db "BRCS", 0 -.db "BREQ", 0 -.db "BRMI", 0 -.db "BRVS", 0 -.db "BRLT", 0 -.db "BRHS", 0 -.db "BRTS", 0 -.db "BRIE", 0 -.db "BRCC", 0 -.db "BRNE", 0 -.db "BRPL", 0 -.db "BRVC", 0 -.db "BRGE", 0 -.db "BRHC", 0 -.db "BRTC", 0 -.db "BRID", 0 -.equ I_BRBS 16 -.db "BRBS", 0 -.db "BRBC", 0 -.equ I_LD 18 -.db "LD", 0 -.db "ST", 0 -; Rd(5) + Rr(5) (from here, instrTbl8) -.equ I_ADC 20 -.db "ADC", 0 -.db "ADD", 0 -.db "AND", 0 -.db "ASR", 0 -.db "BCLR", 0 -.db "BLD", 0 -.db "BREAK", 0 -.db "BSET", 0 -.db "BST", 0 -.db "CLC", 0 -.db "CLH", 0 -.db "CLI", 0 -.db "CLN", 0 -.db "CLR", 0 -.db "CLS", 0 -.db "CLT", 0 -.db "CLV", 0 -.db "CLZ", 0 -.db "COM", 0 -.db "CP", 0 -.db "CPC", 0 -.db "CPSE", 0 -.db "DEC", 0 -.db "EICALL", 0 -.db "EIJMP", 0 -.db "EOR", 0 -.db "ICALL", 0 -.db "IJMP", 0 -.db "IN", 0 -.db "INC", 0 -.db "LAC", 0 -.db "LAS", 0 -.db "LAT", 0 -.db "LSL", 0 -.db "LSR", 0 -.db "MOV", 0 -.db "MUL", 0 -.db "NEG", 0 -.db "NOP", 0 -.db "OR", 0 -.db "OUT", 0 -.db "POP", 0 -.db "PUSH", 0 -.db "RET", 0 -.db "RETI", 0 -.db "ROR", 0 -.db "SBC", 0 -.db "SBRC", 0 -.db "SBRS", 0 -.db "SEC", 0 -.db "SEH", 0 -.db "SEI", 0 -.db "SEN", 0 -.db "SER", 0 -.db "SES", 0 -.db "SET", 0 -.db "SEV", 0 -.db "SEZ", 0 -.db "SLEEP", 0 -.db "SUB", 0 -.db "SWAP", 0 -.db "TST", 0 -.db "WDR", 0 -.db "XCH", 0 -.equ I_ANDI 84 -.db "ANDI", 0 -.db "CBR", 0 -.db "CPI", 0 -.db "LDI", 0 -.db "ORI", 0 -.db "SBCI", 0 -.db "SBR", 0 -.db "SUBI", 0 -.equ I_RCALL 92 -.db "RCALL", 0 -.db "RJMP", 0 -.equ I_CBI 94 -.db "CBI", 0 -.db "SBI", 0 -.db "SBIC", 0 -.db "SBIS", 0 -; 32-bit -; ZASM limitation: CALL and JMP constants are 22-bit. In ZASM, we limit -; ourselves to 16-bit. Supporting 22-bit would incur a prohibitive complexity -; cost. As they say, 64K words ought to be enough for anybody. -.equ I_CALL 98 -.db "CALL", 0 -.db "JMP", 0 -.db 0xff - -; Instruction table -; -; A table row starts with the "argspecs+flags" byte, followed by two upcode -; bytes. -; -; The argspecs+flags byte is separated in two nibbles: Low nibble is a 4bit -; index (1-based, 0 means no arg) in the argSpecs table. High nibble is for -; flags. Meaning: -; -; Bit 7: Arguments swapped. For example, if we have this bit set on the argspec -; row 'A', 'R', then what will actually be read is 'R', 'A'. The -; arguments destination will be, hum, de-swapped, that is, 'A' is going -; in H and 'R' is going in L. This is used, for example, with IN and OUT. -; IN has a Rd(5), A(6) signature. OUT could have the same signature, but -; AVR's mnemonics has those args reversed for more consistency -; (destination is always the first arg). The goal of this flag is to -; allow this kind of syntactic sugar with minimal complexity. -; -; Bit 6: Second arg is a copy of the first -; Bit 5: Second arg is inverted (complement) - -; In the same order as in instrNames -instrTbl: -; Regular processing: Rd with second arg having its 4 low bits placed in C's -; 3:0 bits and the 4 high bits being place in B's 4:1 bits -; No args are also there. -.db 0x02, 0b00011100, 0x00 ; ADC Rd, Rr -.db 0x02, 0b00001100, 0x00 ; ADD Rd, Rr -.db 0x02, 0b00100000, 0x00 ; AND Rd, Rr -.db 0x01, 0b10010100, 0b00000101 ; ASR Rd -.db 0x0b, 0b10010100, 0b10001000 ; BCLR s, k -.db 0x05, 0b11111000, 0x00 ; BLD Rd, b -.db 0x00, 0b10010101, 0b10011000 ; BREAK -.db 0x0b, 0b10010100, 0b00001000 ; BSET s, k -.db 0x05, 0b11111010, 0x00 ; BST Rd, b -.db 0x00, 0b10010100, 0b10001000 ; CLC -.db 0x00, 0b10010100, 0b11011000 ; CLH -.db 0x00, 0b10010100, 0b11111000 ; CLI -.db 0x00, 0b10010100, 0b10101000 ; CLN -.db 0x41, 0b00100100, 0x00 ; CLR Rd (Bit 6) -.db 0x00, 0b10010100, 0b11001000 ; CLS -.db 0x00, 0b10010100, 0b11101000 ; CLT -.db 0x00, 0b10010100, 0b10111000 ; CLV -.db 0x00, 0b10010100, 0b10011000 ; CLZ -.db 0x01, 0b10010100, 0b00000000 ; COM Rd -.db 0x02, 0b00010100, 0x00 ; CP Rd, Rr -.db 0x02, 0b00000100, 0x00 ; CPC Rd, Rr -.db 0x02, 0b00010000, 0x00 ; CPSE Rd, Rr -.db 0x01, 0b10010100, 0b00001010 ; DEC Rd -.db 0x00, 0b10010101, 0b00011001 ; EICALL -.db 0x00, 0b10010100, 0b00011001 ; EIJMP -.db 0x02, 0b00100100, 0x00 ; EOR Rd, Rr -.db 0x00, 0b10010101, 0b00001001 ; ICALL -.db 0x00, 0b10010100, 0b00001001 ; IJMP -.db 0x07, 0b10110000, 0x00 ; IN Rd, A -.db 0x01, 0b10010100, 0b00000011 ; INC Rd -.db 0x01, 0b10010010, 0b00000110 ; LAC Rd -.db 0x01, 0b10010010, 0b00000101 ; LAS Rd -.db 0x01, 0b10010010, 0b00000111 ; LAT Rd -.db 0x41, 0b00001100, 0x00 ; LSL Rd -.db 0x01, 0b10010100, 0b00000110 ; LSR Rd -.db 0x02, 0b00101100, 0x00 ; MOV Rd, Rr -.db 0x02, 0b10011100, 0x00 ; MUL Rd, Rr -.db 0x01, 0b10010100, 0b00000001 ; NEG Rd -.db 0x00, 0b00000000, 0b00000000 ; NOP -.db 0x02, 0b00101000, 0x00 ; OR Rd, Rr -.db 0x87, 0b10111000, 0x00 ; OUT A, Rr (Bit 7) -.db 0x01, 0b10010000, 0b00001111 ; POP Rd -.db 0x01, 0b10010010, 0b00001111 ; PUSH Rd -.db 0x00, 0b10010101, 0b00001000 ; RET -.db 0x00, 0b10010101, 0b00011000 ; RETI -.db 0x01, 0b10010100, 0b00000111 ; ROR Rd -.db 0x02, 0b00001000, 0x00 ; SBC Rd, Rr -.db 0x05, 0b11111100, 0x00 ; SBRC Rd, b -.db 0x05, 0b11111110, 0x00 ; SBRS Rd, b -.db 0x00, 0b10010100, 0b00001000 ; SEC -.db 0x00, 0b10010100, 0b01011000 ; SEH -.db 0x00, 0b10010100, 0b01111000 ; SEI -.db 0x00, 0b10010100, 0b00101000 ; SEN -.db 0x0a, 0b11101111, 0b00001111 ; SER Rd -.db 0x00, 0b10010100, 0b01001000 ; SES -.db 0x00, 0b10010100, 0b01101000 ; SET -.db 0x00, 0b10010100, 0b00111000 ; SEV -.db 0x00, 0b10010100, 0b00011000 ; SEZ -.db 0x00, 0b10010101, 0b10001000 ; SLEEP -.db 0x02, 0b00011000, 0x00 ; SUB Rd, Rr -.db 0x01, 0b10010100, 0b00000010 ; SWAP Rd -.db 0x41, 0b00100000, 0x00 ; TST Rd (Bit 6) -.db 0x00, 0b10010101, 0b10101000 ; WDR -.db 0x01, 0b10010010, 0b00000100 ; XCH Rd -; Rd(4) + K(8): XXXXKKKK ddddKKKK -.db 0x04, 0b01110000, 0x00 ; ANDI Rd, K -.db 0x24, 0b01110000, 0x00 ; CBR Rd, K (Bit 5) -.db 0x04, 0b00110000, 0x00 ; CPI Rd, K -.db 0x04, 0b11100000, 0x00 ; LDI Rd, K -.db 0x04, 0b01100000, 0x00 ; ORI Rd, K -.db 0x04, 0b01000000, 0x00 ; SBCI Rd, K -.db 0x04, 0b01100000, 0x00 ; SBR Rd, K -.db 0x04, 0b01010000, 0x00 ; SUBI Rd, K -; k(12): XXXXkkkk kkkkkkkk -.db 0x08, 0b11010000, 0x00 ; RCALL k -.db 0x08, 0b11000000, 0x00 ; RJMP k -; A(5) + bit: XXXXXXXX AAAAAbbb -.db 0x09, 0b10011000, 0x00 ; CBI A, b -.db 0x09, 0b10011010, 0x00 ; SBI A, b -.db 0x09, 0b10011001, 0x00 ; SBIC A, b -.db 0x09, 0b10011011, 0x00 ; SBIS A, b -; k(16) (well, k(22)...) -.db 0x08, 0b10010100, 0b00001110 ; CALL k -.db 0x08, 0b10010100, 0b00001100 ; JMP k - -; Same signature as getInstID in instr.asm -; Reads string in (HL) and returns the corresponding ID (I_*) in A. Sets Z if -; there's a match. -getInstID: - push bc - push hl - push de - ex de, hl ; DE makes a better needle - ; haystack. -1 because we inc HL at the beginning of the loop - ld hl, instrNames-1 - ld b, 0xff ; index counter -.loop: - inc b - inc hl - ld a, (hl) - inc a ; check if 0xff - jr z, .notFound - call strcmpIN - jr nz, .loop - ; found! - ld a, b ; index - cp a ; ensure Z -.end: - pop de - pop hl - pop bc - ret -.notFound: - dec a ; unset Z - jr .end - -; Same signature as parseInstruction in instr.asm -; Parse instruction specified in A (I_* const) with args in I/O and write -; resulting opcode(s) in I/O. -; Sets Z on success. On error, A contains an error code (ERR_*) -parseInstruction: - ; *** Step 1: initialization - ; Except setting up our registers, we also check if our index < I_ADC. - ; If we are, we skip regular processing for the .BR processing, which - ; is a bit special. - ; During this processing, BC is used as the "upcode WIP" register. It's - ; there that we send our partial values until they're ready to spit to - ; I/O. - ld bc, 0 - ld e, a ; Let's keep that instrID somewhere safe - ; First, let's fetch our table row - cp I_LD - jp c, .BR ; BR is special, no table row - jp z, .LD ; LD is special - cp I_ADC - jp c, .ST ; ST is special - - ; *** Step 2: parse arguments - sub I_ADC ; Adjust index for table - ; Our row is at instrTbl + (A * 3) - ld hl, instrTbl - call addHL - sla a ; A * 2 - call addHL ; (HL) is our row - ld a, (hl) - push hl \ pop ix ; IX is now our tblrow - ld hl, 0 - or a - jp z, .spit ; No arg? spit right away - and 0xf ; lower nibble - dec a ; argspec index is 1-based - ld hl, argSpecs - sla a ; A * 2 - call addHL ; (HL) is argspec row - ld d, (hl) - inc hl - ld a, (hl) - ld h, d - ld l, a ; H and L contain specs now - bit 7, (ix) - call nz, .swapHL ; Bit 7 set, swap H and L - call _parseArgs - ret nz - ; *** Step 3: place arguments in binary upcode and spit. - ; (IX) is table row - ; Parse arg values now in H and L - ; InstrID is E - bit 7, (ix) - call nz, .swapHL ; Bit 7 set, swap H and L again! - bit 6, (ix) - call nz, .cpHintoL ; Bit 6 set, copy H into L - bit 5, (ix) - call nz, .invL ; Bit 5 set, invert L - ld a, e ; InstrID - cp I_ANDI - jr c, .spitRegular - cp I_RCALL - jr c, .spitRdK8 - cp I_CBI - jr c, .spitk12 - cp I_CALL - jr c, .spitA5Bit - ; Spit k(16) - call .spit ; spit 16-bit const upcode - ; divide HL by 2 (PC deals with words, not bytes) - srl h \ rr l - ; spit 16-bit K, LSB first - ld a, l - call ioPutB - ld a, h - jp ioPutB - -.spitRegular: - ; Regular process which places H and L, ORring it with upcode. Works - ; in most cases. - call .placeRd - call .placeRr - jr .spit - -.spitRdK8: - call .placeRd - call .placeRr - rr b ; K(8) start at B's 1st bit, not 2nd - jr .spit - -.spitk12: - ; k(12) in HL - ; We're doing the same dance as in _readk7. See comments there. - call zasmIsFirstPass - jr z, .spit - ld de, 0xfff - add hl, de - jp c, unsetZ ; Carry? number is way too high. - ex de, hl - call zasmGetPC ; --> HL - inc hl \ inc hl - ex de, hl - sbc hl, de - jp c, unsetZ ; Carry? error - ld de, 0xfff - sbc hl, de - ; We're within bounds! Now, divide by 2 - ld a, l - rr h \ rra - ; LSB in A - ld c, a - ld a, h - and 0xf - ld b, a - jr .spit -.spitA5Bit: - ld a, h - sla a \ rla \ rla - or l - ld c, a - jr .spit - -.spit: - ; LSB is spit *before* MSB - ld a, (ix+2) - or c - call ioPutB -.spitMSB: - ld a, (ix+1) - or b - call ioPutB - xor a ; ensure Z, set success - ret - -; Spit a branching mnemonic. -.BR: - ; While we have our index in A, let's settle B straight: Our base - ; upcode is 0b11110000 for "bit set" types and 0b11110100 for "bit - ; clear" types. However, we'll have 2 left shift operation done on B - ; later on, so we need those bits shifted right. - ld b, 0b111100 - cp I_BRBS - jr z, .rdBRBS - jr nc, .rdBRBC - ; We have an alias. Our "sss" value is index & 0b111 - ; Before we get rid of that 3rd bit, let's see, is it set? if yes, we'll - ; want to increase B - bit 3, a - jr z, .skip1 ; 3rd bit unset - inc b -.skip1: - and 0b111 - ld c, a ; can't store in H now, (HL) is used - ld h, 7 - ld l, 0 - call _parseArgs - ret nz - ; ok, now we can - ld l, h ; k in L - ld h, c ; bit in H -.spitBR2: - ; bit in H, k in L. - ; Our value in L is the number of relative *bytes*. The value we put - ; there is the number of words. Therefore, relevant bits are 7:1 - ld a, l - sla a \ rl b - sla a \ rl b - and 0b11111000 - ; k is now shifted by 3, two of those bits being in B. Let's OR A and - ; H and we have our LSB ready to go. - or h - call ioPutB - ; Good! MSB now. B is already good to go. - ld a, b - jp ioPutB -.rdBRBC: - ; In addition to reading "sss", we also need to inc B so that our base - ; upcode becomes 0b111101 - inc b -.rdBRBS: - ld h, 'b' - ld l, 7 - call _parseArgs - ret nz - ; bit in H, k in L. - jr .spitBR2 - -.LD: - ld h, 'R' - ld l, 'z' - call _parseArgs - ret nz - ld d, 0b10000000 - jr .LDST -.ST: - ld h, 'z' - ld l, 'R' - call _parseArgs - ret nz - ld d, 0b10000010 - call .swapHL - ; continue to .LDST - -.LDST: - ; Rd in H, Z in L, base upcode in D - call .placeRd - ; We're spitting LSB first, so let's compose it. - ld a, l - and 0b00001111 - or c - call ioPutB - ; Now, MSB's bit 4 is L's bit 4. How convenient! - ld a, l - and 0b00010000 - or d - or b - ; MSB composed! - call ioPutB - cp a ; ensure Z - ret - -; local routines -; place number in H in BC at position .......d dddd.... -; BC is assumed to be 0 -.placeRd: - sla h \ rl h \ rl h \ rl h ; last RL H might set carry - rl b - ld c, h - ret - -; place number in L in BC at position ...rrrr. ....rrrr -; BC is assumed to be either 0 or to be set by .placeRd, that is, that the -; high 4 bits of C and lowest bit of B will be preserved. -.placeRr: - ; let's start with the 4 lower bits - ld a, l - and 0x0f - or c - ld c, a - ld a, l - ; and now those high 4 bits which go in B. - and 0xf0 - rra \ rra \ rra - or b - ld b, a - ret - -.swapHL: - ld a, h - ld h, l - ld l, a - ret - -.cpHintoL: - ld l, h - ret - -.invL: - ld a, l - cpl - ld l, a - ret - -; Argspecs: two bytes describing the arguments that are accepted. Possible -; values: -; -; 0 - None -; 7 - a k(7) address, relative to PC, *in bytes* (divide by 2 before writing) -; 8 - a K(8) value -; 'a' - A 5-bit I/O port value -; 'A' - A 6-bit I/O port value -; 'b' - a 0-7 bit value -; 'D' - A double-length number which will fill whole HL. -; 'R' - an r5 value: r0-r31 -; 'r' - an r4 value: r16-r31 -; 'z' - an indirect register (X, Y or Z), with our without post-inc/pre-dec -; indicator. This will result in a 5-bit number, from which we can place -; bits 3:0 to upcode's 3:0 and bit 4 at upcode's 12 in LD and ST. -; -; All arguments accept expressions, even 'r' ones: in 'r' args, we start by -; looking if the arg starts with 'r' or 'R'. If yes, it's a simple 'rXX' value, -; if not, we try parsing it as an expression and validate that it falls in the -; correct 0-31 or 16-31 range -argSpecs: - .db 'R', 0 ; Rd(5) - .db 'R', 'R' ; Rd(5) + Rr(5) - .db 7, 0 ; k(7) - .db 'r', 8 ; Rd(4) + K(8) - .db 'R', 'b' ; Rd(5) + bit - .db 'b', 7 ; bit + k(7) - .db 'R', 'A' ; Rd(5) + A(6) - .db 'D', 0 ; K(12) - .db 'a', 'b' ; A(5) + bit - .db 'r', 0 ; Rd(4) - .db 'b', 0 ; bit - -; Parse arguments from I/O according to specs in HL -; H for first spec, L for second spec -; Puts the results in HL -; First arg in H, second in L. -; This routine is not used in all cases, some ops don't fit this pattern well -; and thus parse their args themselves. -; Z for success. -_parseArgs: - ; For the duration of the routine, argspec is in DE and final MSB is - ; in BC. We place result in HL at the end. - push de - push bc - ld bc, 0 - ex de, hl ; argspecs now in DE - call readWord - jr nz, .end - ld a, d - call .parse - jr nz, .end - ld b, a - ld a, e - or a - jr z, .end ; no arg - call readComma - jr nz, .end - call readWord - jr nz, .end - ld a, e - call .parse - jr nz, .end - ; we're done with (HL) now - ld c, a - cp a ; ensure Z -.end: - ld h, b - ld l, c - pop bc - pop de - ret - -; Parse a single arg specified in A and returns its value in A -; Z for success -.parse: - cp 'R' - jr z, _readR5 - cp 'r' - jr z, _readR4 - cp 'b' - jr z, _readBit - cp 'A' - jr z, _readA6 - cp 'a' - jr z, _readA5 - cp 7 - jr z, _readk7 - cp 8 - jr z, _readK8 - cp 'D' - jr z, _readDouble - cp 'z' - jp z, _readz - ret ; something's wrong - -_readBit: - ld a, 7 - jp _readExpr - -_readA6: - ld a, 0x3f - jp _readExpr - -_readA5: - ld a, 0x1f - jp _readExpr - -_readK8: - ld a, 0xff - jp _readExpr - -_readDouble: - push de - call parseExpr - jr nz, .end - ld b, d - ld c, e - ; BC is already set. For good measure, let's set A to BC's MSB - ld a, b -.end: - pop de - ret - -_readk7: - push hl - push de - call parseExpr - jr nz, .end - ; If we're in first pass, stop now. The value of HL doesn't matter and - ; truncation checks might falsely fail. - call zasmIsFirstPass - jr z, .end - ; DE contains an absolute value. Turn this into a -64/+63 relative - ; value by subtracting PC from it. However, before we do that, let's - ; add 0x7f to it, which we'll remove later. This will simplify bounds - ; checks. (we use 7f instead of 3f because we deal in bytes here, not - ; in words) - ld hl, 0x7f - add hl, de ; Carry cleared - ex de, hl - call zasmGetPC ; --> HL - ; The relative value is actually not relative to current PC, but to - ; PC after the execution of this branching op. Increase HL by 2. - inc hl \ inc hl - ex de, hl - sbc hl, de - jr c, .err ; Carry? error - ld de, 0x7f - sbc hl, de - ; We're within bounds! However, our value in L is the number of - ; relative *bytes*. - ld a, l - cp a ; ensure Z -.end: - pop de - pop hl - ret -.err: - call unsetZ - jr .end - -_readR4: - call _readR5 - ret nz - ; has to be in the 16-31 range - sub 0x10 - jp c, unsetZ - cp a ; ensure Z - ret - -; read a rXX argument and return register number in A. -; Set Z for success. -_readR5: - push de - ld a, (hl) - call upcase - cp 'X' - jr z, .rdXYZ - cp 'Y' - jr z, .rdXYZ - cp 'Z' - jr z, .rdXYZ - cp 'R' - jr nz, .end ; not a register - inc hl - call parseDecimal - jr nz, .end - ld a, 31 - call _DE2A -.end: - pop de - ret -.rdXYZ: - ; First, let's get a base value, that is, (A-'X'+26)*2, because XL, our - ; lowest register, is equivalent to r26. - sub 'X' - rla ; no carry from sub - add a, 26 - ld d, a ; store that - inc hl - ld a, (hl) - call upcase - cp 'H' - jr nz, .skip1 - ; second char is 'H'? our value is +1 - inc d - jr .skip2 -.skip1: - cp 'L' - jr nz, .end ; not L either? then it's not good -.skip2: - ; Good, we have our final value in D and we're almost sure it's a valid - ; register. Our only check left is that the 3rd char is a null. - inc hl - ld a, (hl) - or a - jr nz, .end - ; we're good - ld a, d - jr .end - -; Put DE's LSB into A and, additionally, ensure that the new value is <= -; than what was previously in A. -; Z for success. -_DE2A: - cp e - jp c, unsetZ ; A < E - ld a, d - or a - ret nz ; should be zero - ld a, e - ; Z set from "or a" - ret - -; Read expr and return success only if result in under number given in A -; Z for success -_readExpr: - push de - push bc - ld b, a - call parseExpr - jr nz, .end - ld a, b - call _DE2A - jr nz, .end - or c - ld c, a - cp a ; ensure Z -.end: - pop bc - pop de - ret - -; Parse one of the following: X, Y, Z, X+, Y+, Z+, -X, -Y, -Z. -; For each of those values, return a 5-bit value than can then be interleaved -; with LD or ST upcodes. -_readz: - call strlen - cp 3 - jp nc, unsetZ ; string too long - ; Let's load first char in A and second in A'. This will free HL - ld a, (hl) - ex af, af' - inc hl - ld a, (hl) ; Good, HL is now free - ld hl, .tblStraight - or a - jr z, .parseXYZ ; Second char null? We have a single char - ; Maybe + - cp '+' - jr nz, .skip - ; We have a + - ld hl, .tblInc - jr .parseXYZ -.skip: - ; Maybe a - - ex af, af' - cp '-' - ret nz ; we have nothing - ; We have a - - ld hl, .tblDec - ; continue to .parseXYZ -.parseXYZ: - ; We have X, Y or Z in A' - ex af, af' - call upcase - ; Now, let's place HL - cp 'X' - jr z, .fetch - inc hl - cp 'Y' - jr z, .fetch - inc hl - cp 'Z' - ret nz ; error -.fetch: - ld a, (hl) - ; Z already set from earlier cp - ret - -.tblStraight: - .db 0b11100 ; X - .db 0b01000 ; Y - .db 0b00000 ; Z -.tblInc: - .db 0b11101 ; X+ - .db 0b11001 ; Y+ - .db 0b10001 ; Z+ -.tblDec: - .db 0b11110 ; -X - .db 0b11010 ; -Y - .db 0b10010 ; -Z - diff --git a/apps/zasm/const.asm b/apps/zasm/const.asm deleted file mode 100644 index d405473..0000000 --- a/apps/zasm/const.asm +++ /dev/null @@ -1,25 +0,0 @@ -; *** Errors *** -; We start error at 0x10 to avoid overlapping with shell errors -; Unknown instruction or directive -.equ ERR_UNKNOWN 0x11 - -; Bad argument: Doesn't match any constant argspec or, if an expression, -; contains references to undefined symbols. -.equ ERR_BAD_ARG 0x12 - -; Code is badly formatted (comma without a following arg, unclosed quote, etc.) -.equ ERR_BAD_FMT 0x13 - -; Value specified doesn't fit in its destination byte or word -.equ ERR_OVFL 0x14 - -.equ ERR_FILENOTFOUND 0x15 - -; Duplicate symbol -.equ ERR_DUPSYM 0x16 - -; Out of memory -.equ ERR_OOM 0x17 - -; *** Other *** -.equ ZASM_DEBUG_PORT 42 diff --git a/apps/zasm/directive.asm b/apps/zasm/directive.asm deleted file mode 100644 index 270c9a1..0000000 --- a/apps/zasm/directive.asm +++ /dev/null @@ -1,336 +0,0 @@ -; *** CONSTS *** - -.equ D_DB 0x00 -.equ D_DW 0x01 -.equ D_EQU 0x02 -.equ D_ORG 0x03 -.equ D_FIL 0x04 -.equ D_OUT 0x05 -.equ D_INC 0x06 -.equ D_BIN 0x07 -.equ D_BAD 0xff - -; *** Variables *** -; Result of the last .equ evaluation. Used for "@" symbol. -.equ DIREC_LASTVAL DIREC_RAMSTART -.equ DIREC_SCRATCHPAD DIREC_LASTVAL+2 -.equ DIREC_RAMEND DIREC_SCRATCHPAD+SCRATCHPAD_SIZE -; *** CODE *** - -; 3 bytes per row, fill with zero -dirNames: - .db "DB", 0 - .db "DW", 0 - .db "EQU" - .db "ORG" - .db "FIL" - .db "OUT" - .db "INC" - .db "BIN" - -; This is a list of handlers corresponding to indexes in dirNames -dirHandlers: - .dw handleDB - .dw handleDW - .dw handleEQU - .dw handleORG - .dw handleFIL - .dw handleOUT - .dw handleINC - .dw handleBIN - -handleDB: - push de - push hl -.loop: - call readWord - jr nz, .badfmt - ld hl, scratchpad - call enterDoubleQuotes - jr z, .stringLiteral - call parseExpr - jr nz, .badarg - ld a, d - or a ; cp 0 - jr nz, .overflow ; not zero? overflow - ld a, e - call ioPutB - jr nz, .ioError -.stopStrLit: - call readComma - jr z, .loop - cp a ; ensure Z -.end: - pop hl - pop de - ret -.ioError: - ld a, SHELL_ERR_IO_ERROR - jr .error -.badfmt: - ld a, ERR_BAD_FMT - jr .error -.badarg: - ld a, ERR_BAD_ARG - jr .error -.overflow: - ld a, ERR_OVFL -.error: - or a ; unset Z - jr .end - -.stringLiteral: - ld a, (hl) - inc hl - or a ; when we encounter 0, that was what used to - jr z, .stopStrLit ; be our closing quote. Stop. - ; Normal character, output - call ioPutB - jr nz, .ioError - jr .stringLiteral - -handleDW: - push de - push hl -.loop: - call readWord - jr nz, .badfmt - ld hl, scratchpad - call parseExpr - jr nz, .badarg - ld a, e - call ioPutB - jr nz, .ioError - ld a, d - call ioPutB - jr nz, .ioError - call readComma - jr z, .loop - cp a ; ensure Z -.end: - pop hl - pop de - ret -.ioError: - ld a, SHELL_ERR_IO_ERROR - jr .error -.badfmt: - ld a, ERR_BAD_FMT - jr .error -.badarg: - ld a, ERR_BAD_ARG -.error: - or a ; unset Z - jr .end - -handleEQU: - call zasmIsLocalPass ; Are we in local pass? Then ignore all .equ. - jr z, .skip ; they mess up duplicate symbol detection. - ; We register constants on both first and second pass for one little - ; reason: .org. Normally, we'd register constants on second pass only - ; so that we have values for forward label references, but we need .org - ; to be effective during the first pass and .org needs to support - ; expressions. So, we double-parse .equ, clearing the const registry - ; before the second pass. - push hl - push de - push bc - ; Read our constant name - call readWord - jr nz, .badfmt - ; We can't register our symbol yet: we don't have our value! - ; Let's copy it over. - ld de, DIREC_SCRATCHPAD - ld bc, SCRATCHPAD_SIZE - ldir - - ; Now, read the value associated to it - call readWord - jr nz, .badfmt - ld hl, scratchpad - call parseExpr - jr nz, .badarg - ld hl, DIREC_SCRATCHPAD - ; Save value in "@" special variable - ld (DIREC_LASTVAL), de - call symRegisterConst ; A and Z set - jr z, .end ; success - ; register ended up in error. We need to figure which error. If it's - ; a duplicate error, we ignore it and return success because, as per - ; ".equ" policy, it's fine to define the same const twice. The first - ; value has precedence. - cp ERR_DUPSYM - ; whatever the value of Z, it's the good one, return - jr .end -.badfmt: - ld a, ERR_BAD_FMT - jr .error -.badarg: - ld a, ERR_BAD_ARG -.error: - call unsetZ -.end: - pop bc - pop de - pop hl - ret -.skip: - ; consume args and return - call readWord - jp readWord - -handleORG: - push de - call readWord - jr nz, .badfmt - call parseExpr - jr nz, .badarg - ex de, hl - ld (DIREC_LASTVAL), hl - call zasmSetOrg - cp a ; ensure Z -.end: - pop de - ret -.badfmt: - ld a, ERR_BAD_FMT - jr .error -.badarg: - ld a, ERR_BAD_ARG -.error: - or a ; unset Z - jr .end - -handleFIL: - call readWord - jr nz, .badfmt - call parseExpr - jr nz, .badarg - ld a, d - cp 0xd0 - jr nc, .overflow -.loop: - ld a, d - or e - jr z, .loopend - xor a - call ioPutB - jr nz, .ioError - dec de - jr .loop -.loopend: - cp a ; ensure Z - ret -.ioError: - ld a, SHELL_ERR_IO_ERROR - jp unsetZ -.badfmt: - ld a, ERR_BAD_FMT - jp unsetZ -.badarg: - ld a, ERR_BAD_ARG - jp unsetZ -.overflow: - ld a, ERR_OVFL - jp unsetZ - -handleOUT: - push de - push hl - ; Read our expression - call readWord - jr nz, .badfmt - call zasmIsFirstPass ; No .out during first pass - jr z, .end - ld hl, scratchpad - call parseExpr - jr nz, .badarg - ld a, d - out (ZASM_DEBUG_PORT), a - ld a, e - out (ZASM_DEBUG_PORT), a - jr .end -.badfmt: - ld a, ERR_BAD_FMT - jr .error -.badarg: - ld a, ERR_BAD_ARG -.error: - or a ; unset Z -.end: - pop hl - pop de - ret - -handleINC: - call readWord - jr nz, .badfmt - ; HL points to scratchpad - call enterDoubleQuotes - jr nz, .badfmt - call ioOpenInclude - jr nz, .badfn - cp a ; ensure Z - ret -.badfmt: - ld a, ERR_BAD_FMT - jr .error -.badfn: - ld a, ERR_FILENOTFOUND -.error: - call unsetZ - ret - -handleBIN: - call readWord - jr nz, .badfmt - ; HL points to scratchpad - call enterDoubleQuotes - jr nz, .badfmt - call ioSpitBin - jr nz, .badfn - cp a ; ensure Z - ret -.badfmt: - ld a, ERR_BAD_FMT - jr .error -.badfn: - ld a, ERR_FILENOTFOUND -.error: - call unsetZ - ret - -; Reads string in (HL) and returns the corresponding ID (D_*) in A. Sets Z if -; there's a match. -getDirectiveID: - ld a, (hl) - cp '.' - ret nz - push hl - push bc - push de - inc hl - ld b, D_BIN+1 ; D_BIN is last - ld c, 3 - ld de, dirNames - call findStringInList - pop de - pop bc - pop hl - ret - -; Parse directive specified in A (D_* const) with args in I/O and act in -; an appropriate manner. If the directive results in writing data at its -; current location, that data is directly written through ioPutB. -; Each directive has the same return value pattern: Z on success, not-Z on -; error, A contains the error number (ERR_*). -parseDirective: - push de - ; double A to have a proper offset in dirHandlers - add a, a - ld de, dirHandlers - call addDE - call intoDE - push de \ pop ix - pop de - jp (ix) diff --git a/apps/zasm/glue.asm b/apps/zasm/glue.asm deleted file mode 100644 index 5b0dd28..0000000 --- a/apps/zasm/glue.asm +++ /dev/null @@ -1,88 +0,0 @@ -; zasm -; -; Reads input from specified blkdev ID, assemble the binary in two passes and -; spit the result in another specified blkdev ID. -; -; We don't buffer the whole source in memory, so we need our input blkdev to -; support Seek so we can read the file a second time. So, for input, we need -; GetB and Seek. -; -; For output, we only need PutB. Output doesn't start until the second pass. -; -; The goal of the second pass is to assign values to all symbols so that we -; can have forward references (instructions referencing a label that happens -; later). -; -; Labels and constants are both treated the same way, that is, they can be -; forward-referenced in instructions. ".equ" directives, however, are evaluated -; during the first pass so forward references are not allowed. -; -; *** Requirements *** -; strncmp -; upcase -; findchar -; blkSel -; blkSet -; fsFindFN -; fsOpen -; fsGetB -; _blkGetB -; _blkPutB -; _blkSeek -; _blkTell -; printstr -; printcrlf - -.inc "user.h" - -; *** Overridable consts *** -; NOTE: These limits below are designed to be *just* enough for zasm to assemble -; itself. Considering that this app is Collapse OS' biggest app, it's safe to -; assume that it will be enough for many many use cases. If you need to compile -; apps with lots of big symbols, you'll need to adjust these. -; With these default settings, zasm runs with less than 0x1800 bytes of RAM! - -; Maximum number of symbols we can have in the global and consts registry -.equ ZASM_REG_MAXCNT 0xff - -; Maximum number of symbols we can have in the local registry -.equ ZASM_LREG_MAXCNT 0x20 - -; Size of the symbol name buffer size. This is a pool. There is no maximum name -; length for a single symbol, just a maximum size for the whole pool. -; Global labels and consts have the same buf size -.equ ZASM_REG_BUFSZ 0x700 - -; Size of the names buffer for the local context registry -.equ ZASM_LREG_BUFSZ 0x100 - -; ****** - -.inc "err.h" -.inc "ascii.h" -.inc "blkdev.h" -.inc "fs.h" -jp zasmMain - -.inc "core.asm" -.inc "zasm/const.asm" -.inc "lib/util.asm" -.inc "lib/ari.asm" -.inc "lib/parse.asm" -.inc "zasm/util.asm" -.equ IO_RAMSTART USER_RAMSTART -.inc "zasm/io.asm" -.equ TOK_RAMSTART IO_RAMEND -.inc "zasm/tok.asm" -.equ INS_RAMSTART TOK_RAMEND -.inc "zasm/instr.asm" -.equ DIREC_RAMSTART INS_RAMEND -.inc "zasm/directive.asm" -.inc "zasm/parse.asm" -.equ EXPR_PARSE parseNumberOrSymbol -.inc "lib/expr.asm" -.equ SYM_RAMSTART DIREC_RAMEND -.inc "zasm/symbol.asm" -.equ ZASM_RAMSTART SYM_RAMEND -.inc "zasm/main.asm" -USER_RAMSTART: diff --git a/apps/zasm/gluea.asm b/apps/zasm/gluea.asm deleted file mode 100644 index b4b06b9..0000000 --- a/apps/zasm/gluea.asm +++ /dev/null @@ -1,44 +0,0 @@ -; avra -; -; This glue code assembles as assembler for AVR microcontrollers. It looks a -; lot like zasm, but it spits AVR binary. Comments have been stripped, refer -; to glue.asm for details. - -.inc "user.h" - -; *** Overridable consts *** -.equ ZASM_REG_MAXCNT 0xff -.equ ZASM_LREG_MAXCNT 0x20 -.equ ZASM_REG_BUFSZ 0x700 -.equ ZASM_LREG_BUFSZ 0x100 - -; ****** - -.inc "err.h" -.inc "ascii.h" -.inc "blkdev.h" -.inc "fs.h" -jp zasmMain - -.inc "core.asm" -.inc "zasm/const.asm" -.inc "lib/util.asm" -.inc "lib/ari.asm" -.inc "lib/parse.asm" -.inc "zasm/util.asm" -.equ IO_RAMSTART USER_RAMSTART -.inc "zasm/io.asm" -.equ TOK_RAMSTART IO_RAMEND -.inc "zasm/tok.asm" -.inc "zasm/avr.asm" -.equ DIREC_RAMSTART TOK_RAMEND -.inc "zasm/directive.asm" -.inc "zasm/parse.asm" -.equ EXPR_PARSE parseNumberOrSymbol -.inc "lib/expr.asm" -.equ SYM_RAMSTART DIREC_RAMEND -.inc "zasm/symbol.asm" -.equ ZASM_RAMSTART SYM_RAMEND -.inc "zasm/main.asm" -USER_RAMSTART: - diff --git a/apps/zasm/instr.asm b/apps/zasm/instr.asm deleted file mode 100644 index da0ff64..0000000 --- a/apps/zasm/instr.asm +++ /dev/null @@ -1,1234 +0,0 @@ -; *** Consts *** -; Number of rows in the argspec table -.equ ARGSPEC_TBL_CNT 33 -; size in bytes of each row in the primary instructions table -.equ INSTR_TBL_ROWSIZE 6 -; Instruction IDs They correspond to the index of the table in instrNames -.equ I_ADC 0x00 -.equ I_ADD 0x01 -.equ I_AND 0x02 -.equ I_BIT 0x03 -.equ I_CALL 0x04 -.equ I_CCF 0x05 -.equ I_CP 0x06 -.equ I_CPD 0x07 -.equ I_CPDR 0x08 -.equ I_CPI 0x09 -.equ I_CPIR 0x0a -.equ I_CPL 0x0b -.equ I_DAA 0x0c -.equ I_DEC 0x0d -.equ I_DI 0x0e -.equ I_DJNZ 0x0f -.equ I_EI 0x10 -.equ I_EX 0x11 -.equ I_EXX 0x12 -.equ I_HALT 0x13 -.equ I_IM 0x14 -.equ I_IN 0x15 -.equ I_INC 0x16 -.equ I_IND 0x17 -.equ I_INDR 0x18 -.equ I_INI 0x19 -.equ I_INIR 0x1a -.equ I_JP 0x1b -.equ I_JR 0x1c -.equ I_LD 0x1d -.equ I_LDD 0x1e -.equ I_LDDR 0x1f -.equ I_LDI 0x20 -.equ I_LDIR 0x21 -.equ I_NEG 0x22 -.equ I_NOP 0x23 -.equ I_OR 0x24 -.equ I_OTDR 0x25 -.equ I_OTIR 0x26 -.equ I_OUT 0x27 -.equ I_POP 0x28 -.equ I_PUSH 0x29 -.equ I_RES 0x2a -.equ I_RET 0x2b -.equ I_RETI 0x2c -.equ I_RETN 0x2d -.equ I_RL 0x2e -.equ I_RLA 0x2f -.equ I_RLC 0x30 -.equ I_RLCA 0x31 -.equ I_RR 0x32 -.equ I_RRA 0x33 -.equ I_RRC 0x34 -.equ I_RRCA 0x35 -.equ I_RST 0x36 -.equ I_SBC 0x37 -.equ I_SCF 0x38 -.equ I_SET 0x39 -.equ I_SLA 0x3a -.equ I_SRL 0x3b -.equ I_SUB 0x3c -.equ I_XOR 0x3d - -; *** Variables *** -; Args are 3 bytes: argspec, then values of numerical constants (when that's -; appropriate) -.equ INS_CURARG1 INS_RAMSTART -.equ INS_CURARG2 INS_CURARG1+3 -.equ INS_UPCODE INS_CURARG2+3 -.equ INS_RAMEND INS_UPCODE+4 - -; *** Code *** -; Checks whether A is 'N' or 'M' -checkNOrM: - cp 'N' - ret z - cp 'M' - ret - -; Checks whether A is 'n', 'm' -checknm: - cp 'n' - ret z - cp 'm' - ret - -checklxy: - cp 'l' - ret z -; Checks whether A is 'x', 'y' -checkxy: - cp 'x' - ret z - cp 'y' - ret - -; Reads string in (HL) and returns the corresponding ID (I_*) in A. Sets Z if -; there's a match. -getInstID: - push bc - push de - ld b, I_XOR+1 ; I_XOR is the last - ld c, 4 - ld de, instrNames - call findStringInList - pop de - pop bc - ret - -; Parse the string at (HL) and check if it starts with IX+, IY+, IX- or IY-. -; Sets Z if yes, unset if no. On success, A contains either '+' or '-'. -parseIXY: - push hl - ld a, (hl) - call upcase - cp 'I' - jr nz, .end ; Z already unset - inc hl - ld a, (hl) - call upcase - cp 'X' - jr z, .match1 - cp 'Y' - jr z, .match1 - jr .end ; Z already unset -.match1: - ; Alright, we have IX or IY. Let's see if we have + or - next. - inc hl - ld a, (hl) - cp '+' - jr z, .end ; Z is already set - cp '-' - ; The value of Z at this point is our final result -.end: - pop hl - ret - -; find argspec for string at (HL). Returns matching argspec in A. -; Return value 0xff holds a special meaning: arg is not empty, but doesn't match -; any argspec (A == 0 means arg is empty). A return value of 0xff means an -; error. -; -; If the parsed argument is a number constant, 'N' is returned and DE contains -; the value of that constant. -parseArg: - call strlen - or a - ret z ; empty string? A already has our result: 0 - - push bc - push hl - - ld de, argspecTbl - ; DE now points the the "argspec char" part of the entry, but what - ; we're comparing in the loop is the string next to it. Let's offset - ; DE by one so that the loop goes through strings. - inc de - ld b, ARGSPEC_TBL_CNT -.loop1: - ld a, 4 - call strncmpI - jr z, .found ; got it! - ld a, 5 - call addDE - djnz .loop1 - - ; We exhausted the argspecs. Let's see if we're inside parens. - call enterParens - jr z, .withParens - ; (HL) has no parens - call .maybeParseExpr - jr nz, .nomatch - ; We have a proper number in no parens. Number in DE. - ld a, 'N' - jr .end -.withParens: - ld b, 0 ; make sure it doesn't hold '-' - ld c, 'M' ; C holds the argspec type until we reach - ; .numberInParens - ; We have parens. First, let's see if we have a (IX+d) type of arg. - call parseIXY - jr nz, .parseNumberInParens ; not I{X,Y}. just parse number. - ; We have IX+/IY+/IX-/IY-. - ; A contains either '+' or '-'. Save it for later, in B. - ld b, a - inc hl ; (HL) now points to X or Y - ld a, (hl) - call upcase - inc hl ; advance HL to the number part - inc hl ; this is the number - cp 'Y' - jr nz, .notY - ld c, 'y' - jr .parseNumberInParens -.notY: - ld c, 'x' -.parseNumberInParens: - call .maybeParseExpr - jr nz, .nomatch - ; We have a proper number in parens. Number in DE - ; is '-' in B? if yes, we need to negate the low part of DE - ld a, b - cp '-' - jr nz, .dontNegateDE - ; we need to negate the low part of DE - ; TODO: when parsing routines properly support unary negative numbers, - ; We could replace this complicated scheme below with a nice hack where - ; we start parsing our displacement number at the '+' and '-' char. - - ld a, e - neg - ld e, a -.dontNegateDE: - ld a, c ; M, x, or y - jr .end -.nomatch: - ; We get no match - ld a, 0xff - jr .end -.found: - ; found the matching argspec row. Our result is one byte left of DE. - dec de - ld a, (de) - - ; When we have non-numerical args, we set DE to zero to have a clean - ; result. - ld de, 0 - -.end: - pop hl - pop bc - ret - -.maybeParseExpr: - ; Before we try to parse expr in (HL), first check if we're in first - ; pass if we are, skip parseExpr. Most of the time, that parse is - ; harmless, but in some cases it causes false failures. For example, - ; a "-" operator can cause is to falsely overflow and generate - ; truncation error. - ld de, 0 ; in first pass, return a clean zero - call zasmIsFirstPass - ret z - jp parseExpr - -; Returns, with Z, whether A is a groupId -isGroupId: - or a - jp z, unsetZ ; not a group - cp 0xd ; max group id + 1 - jp nc, unsetZ ; >= 0xd? not a group - ; A is a group. ensure Z is set - cp a - ret - -; Find argspec A in group id H. -; Set Z according to whether we found the argspec -; If found, the value in A is the argspec value in the group (its index). -findInGroup: - push bc - push hl - - or a ; is our arg empty? If yes, we have nothing to do - jr z, .notfound - - push af - ld a, h - cp 0xa - jr z, .specialGroupCC - cp 0xb - jr z, .specialGroupABCDEHL - jr nc, .notfound ; > 0xb? not a group - pop af - ; regular group - push de - ld de, argGrpTbl - ; group ids start at 1. decrease it, then multiply by 4 to have a - ; proper offset in argGrpTbl - dec h - push af - ld a, h - rla - rla - call addDE ; At this point, DE points to our group - pop af - ex de, hl ; And now, HL points to the group - pop de - - ld bc, 4 - jr .find - -.specialGroupCC: - ld hl, argGrpCC - jr .specialGroupEnd -.specialGroupABCDEHL: - ld hl, argGrpABCDEHL -.specialGroupEnd: - pop af ; from the push af just before the special group check - ld bc, 8 - -.find: - ; This part is common to regular and special group. We expect HL to - ; point to the group and BC to contain its length. - push bc ; save the start value loop index so we can sub -.loop: - cpi - jr z, .found - jp po, .notfound - jr .loop -.found: - ; we found our result! Now, what we want to put in A is the index of - ; the found argspec. - pop hl ; we pop from the "push bc" above. L is now 4 or 8 - ld a, l - sub c - dec a ; cpi DECs BC even when there's a match, so C == the - ; number of iterations we've made. But our index is - ; zero-based (1 iteration == 0 index). - cp a ; ensure Z is set - jr .end -.notfound: - pop bc ; from the push bc in .find - call unsetZ -.end: - pop hl - pop bc - ret - -; Compare argspec from instruction table in A with argument in (HL). -; IX must point to argspec row. -; For constant args, it's easy: if A == (HL), it's a success. -; If it's not this, then we check if it's a numerical arg. -; If A is a group ID, we do something else: we check that (HL) exists in the -; groupspec (argGrpTbl). Moreover, we go and write the group's "value" (index) -; in (HL+1). This will save us significant processing later in spitUpcode. -; Set Z according to whether we match or not. -matchArg: - cp (hl) - ret z - ; not an exact match. Before we continue: is A zero? Because if it is, - ; we have to stop right here: no match possible. - or a - jr nz, .skip1 ; not a zero, we can continue - ; zero, stop here - cp 1 ; unset Z - ret -.skip1: - ; If our argspec is 'l', then we also match 'x' and 'y' - cp 'l' - jr nz, .skip2 - ; Does it accept IX and IY? - bit 4, (ix+3) - ld a, (hl) - jp nz, checkxy ; bit set: our result is checkxy - ; doesn't accept? then we don't match - jp unsetZ -.skip2: - ; Alright, let's start with a special case. Is it part of the special - ; "BIT" group, 0xc? If yes, we actually expect a number, which will - ; then be ORed like a regular group index. - cp 0xc - jr z, .expectsBIT - ; not an exact match, let's check for numerical constants. - call upcase - call checkNOrM - jr z, .expectsNumber - jr .notNumber -.expectsNumber: - ; Our argument is a number N or M. Never a lower-case version. At this - ; point in the processing, we don't care about whether N or M is upper, - ; we do truncation tests later. So, let's just perform the same == test - ; but in a case-insensitive way instead - cp (hl) - ret ; whether we match or not, the result of Z is - ; the good one. -.expectsBIT: - ld a, (hl) - cp 'N' - inc hl - ld a, (hl) - dec hl - cp 8 - jr c, .isBit ; A < 8 - ; not a bit - or a ; unset Z - ret -.isBit: - cp a ; set Z - ret - -.notNumber: - ; A bit of a delicate situation here: we want A to go in H but also - ; (HL) to go in A. If not careful, we overwrite each other. EXX is - ; necessary to avoid invoving other registers. - push hl - exx - ld h, a - push hl - exx - ld a, (hl) - pop hl - call findInGroup - pop hl - ret nz - ; we found our group? let's write down its "value" in (HL+1). We hold - ; this value in A at the moment. - inc hl - ld (hl), a - dec hl - ret - -; *** Special opcodes *** -; The special upcode handling routines below all have the same signature. -; Instruction row is at IX and we're expected to perform the same task as -; spitUpcode. The number of bytes, however, must go in C instead of A -; No need to preserve HL, DE, BC and IX: it's handled by spitUpcode already. - -; Handle like a regular "JP (IX+d)" except that we refuse any displacement: if -; a displacement is specified, we error out. -handleJPIXY: - ld a, (INS_CURARG1+1) - or a ; numerical argument *must* be zero - jr nz, .error - ; ok, we're good - ld a, 0xe9 ; second upcode - ld (INS_UPCODE), a - ld c, 1 - ret -.error: - ld c, 0 - ret - -handleBITR: - ld b, 0b01000000 - jr _handleBITR -handleSETR: - ld b, 0b11000000 - jr _handleBITR -handleRESR: - ld b, 0b10000000 -_handleBITR: - ; get group value - ld a, (INS_CURARG2+1) ; group value - ld c, a - ; write first upcode - ld a, 0xcb ; first upcode - ld (INS_UPCODE), a - ; get bit value - ld a, (INS_CURARG1+1) ; 0-7 - rlca ; clears cary if any - rla - rla - ; Now we have group value in stack, bit value in A (properly shifted) - ; and we want to OR them together - or c ; Now we have our ORed value - or b ; and with our "base" value and we're good! - ld (INS_UPCODE+1), a - ld c, 2 - ret - -handleIM: - ld a, (INS_CURARG1+1) - cp 0 - jr z, .im0 - cp 1 - jr z, .im1 - cp 2 - jr z, .im2 - ; error - ld c, 0 - ret -.im0: - ld a, 0x46 - jr .proceed -.im1: - ld a, 0x56 - jr .proceed -.im2: - ld a, 0x5e -.proceed: - ld (INS_UPCODE+1), a - ld a, 0xed - ld (INS_UPCODE), a - ld c, 2 - ret - -handleLDIXYn: - ld a, 0x36 ; second upcode - ld (INS_UPCODE), a - ld a, (INS_CURARG1+1) ; IXY displacement - ld (INS_UPCODE+1), a - ld a, (INS_CURARG2+1) ; N - ld (INS_UPCODE+2), a - ld c, 3 - ret - -handleLDIXYr: - ld a, (INS_CURARG2+1) ; group value - or 0b01110000 ; second upcode - ld (INS_UPCODE), a - ld a, (INS_CURARG1+1) ; IXY displacement - ld (INS_UPCODE+1), a - ld c, 2 - ret - -handleLDrIXY: - ld a, (INS_CURARG1+1) ; group value - rlca \ rla \ rla - or 0b01000110 ; second upcode - ld (INS_UPCODE), a - ld a, (INS_CURARG2+1) ; IXY displacement - ld (INS_UPCODE+1), a - ld c, 2 - ret - -handleLDrr: - ; first argument is displaced by 3 bits, second argument is not - ; displaced and we or that with a leading 0b01000000 - ld a, (INS_CURARG1+1) ; group value - rlca - rla - rla - ld c, a ; store it - ld a, (INS_CURARG2+1) ; other group value - or c - or 0b01000000 - ld (INS_UPCODE), a - ld c, 1 - ret - -handleRST: - ld a, (INS_CURARG1+1) - ; verify that A is either 0x08, 0x10, 0x18, 0x20, 0x28, 0x30 or 0x38. - ; Good news: the relevant bits (bits 5:3) are already in place. We only - ; have to verify that they're surrounded by zeroes. - ld c, 0b11000111 - and c - jr nz, .error - ; We're in range. good. - ld a, (INS_CURARG1+1) - or c - ld (INS_UPCODE), a - ld c, 1 - ret -.error: - ld c, 0 - ret - -; Compute the upcode for argspec row at (IX) and arguments in curArg{1,2} and -; writes the resulting upcode to IO. -; A is zero, with Z set, on success. A is non-zero, with Z unset, on error. -spitUpcode: - push de - push hl - push bc - - ; before we begin, are we in a 'l' argspec? Is it flagged for IX/IY - ; acceptance? If yes, a 'x' or 'y' instruction? Check this on both - ; args and if we detect a 'x' or 'y', things are *always* the same: - ; the upcode is exactly the same as its (HL) counterpart except that - ; it is preceeded by 0xdd or 0xfd. If we're 'x' or 'y', then it means - ; that we've already been matched to a 'l' argspec, so after spitting - ; 0xdd or 0xfd, we can continue as normal. - ld a, (ix+1) - call checklxy - jr z, .isl - ld a, (ix+2) - call checklxy - jr nz, .begin ; no point in checking further. -.isl: - ld a, (INS_CURARG1) - cp 'x' - jr z, .isx - cp 'y' - jr z, .isy - ld a, (INS_CURARG2) - cp 'x' - jr z, .isx - cp 'y' - jr z, .isy - jr .begin -.isx: - ld a, 0xdd - call ioPutB - jr .begin -.isy: - ld a, 0xfd - call ioPutB -.begin: - ; Are we a "special instruction"? - bit 5, (ix+3) - jr z, .normalInstr ; not set: normal instruction - ; We are a special instruction. Fetch handler (little endian, remember). - ld l, (ix+4) - ld h, (ix+5) - call callHL - ; We have our result written in INS_UPCODE and C is set. - jp .writeIO - -.normalInstr: - ; we begin by writing our "base upcode", which can be one or two bytes - ld a, (ix+4) ; first upcode - ld (INS_UPCODE), a - ; from this point, DE points to "where we are" in terms of upcode - ; writing. - ld de, INS_UPCODE+1 - ld c, 1 ; C holds our upcode count - - ; Now, let's determine if we have one or two upcode. As a general rule, - ; we simply have to check if (ix+5) == 0, which means one upcode. - ; However, some two-upcodes instructions have a 0 (ix+5) because they - ; expect group OR-ing into it and all other bits are zero. See "RLC r". - ; To handle those cases, we *also* check for Bit 6 in (ix+3). - ld a, (ix+5) ; second upcode - or a ; do we have a second upcode? - jr nz, .twoUpcodes - bit 6, (ix+3) - jr z, .onlyOneUpcode ; not set: single upcode -.twoUpcodes: - ; we have two upcodes - ld (de), a - inc de - inc c -.onlyOneUpcode: - ; now, let's see if we're dealing with a group here - ld a, (ix+1) ; first argspec - call isGroupId - jr z, .firstArgIsGroup - ; First arg not a group. Maybe second is? - ld a, (ix+2) ; 2nd argspec - call isGroupId - jr nz, .writeExtraBytes ; not a group? nothing to do. go to - ; next step: write extra bytes - ; Second arg is group - ld hl, INS_CURARG2 - jr .isGroup -.firstArgIsGroup: - ld hl, INS_CURARG1 -.isGroup: - ; A is a group, good, now let's get its value. HL is pointing to - ; the argument. Our group value is at (HL+1). - inc hl - ld a, (hl) - ; Now, we have our arg "group value" in A. Were going to need to - ; displace it left by the number of steps specified in the table. - push af - ld a, (ix+3) ; displacement bit - and 0xf ; we only use the lower nibble. - ld b, a - pop af - call rlaX - - ; At this point, we have a properly displaced value in A. We'll want - ; to OR it with the opcode. - ; However, we first have to verify whether this ORing takes place on - ; the second upcode or the first. - bit 6, (ix+3) - jr z, .firstUpcode ; not set: first upcode - or (ix+5) ; second upcode - ld (INS_UPCODE+1), a - jr .writeExtraBytes -.firstUpcode: - or (ix+4) ; first upcode - ld (INS_UPCODE), a - jr .writeExtraBytes -.writeExtraBytes: - ; Good, we are probably finished here for many primary opcodes. However, - ; some primary opcodes take 8 or 16 bit constants as an argument and - ; if that's the case here, we need to write it too. - ; We still have our instruction row in IX and we have DE pointing to - ; where we should write next (which could be the second or the third - ; byte of INS_UPCODE). - ld a, (ix+1) ; first argspec - ld hl, INS_CURARG1 - call checkNOrM - jr z, .withWord - call checknm - jr z, .withByte - ld a, (INS_CURARG1) - call checkxy - jr z, .withByte - ld a, (ix+2) ; second argspec - ld hl, INS_CURARG2 - call checkNOrM - jr z, .withWord - call checknm - jr z, .withByte - ld a, (INS_CURARG2) - call checkxy - jr z, .withByte - ; nope, no number, alright, we're finished here - jr .writeIO -.withByte: - inc hl - ; HL points to our number (LSB), with (HL+1) being our MSB which should - ; normally by zero. However, if our instruction is jr or djnz, that - ; number is actually a 2-bytes address that has to be relative to PC, - ; so it's a special case. Let's check for this special case. - bit 7, (ix+3) - jr z, .absoluteValue ; bit not set? regular byte value, - ; Our argument is a relative address ("e" type in djnz and jr). We have - ; to subtract PC from it. - - ; First, check whether we're on first pass. If we are, skip processing - ; below because not having real symbol value makes relative address - ; verification falsely fail. - inc c ; one extra byte is written - call zasmIsFirstPass - jr z, .writeIO - - ; We're on second pass - push de ; Don't let go of this, that's our dest - push hl - call zasmGetPC ; --> HL - ex de, hl - pop hl - call intoHL - dec hl ; what we write is "e-2" - dec hl - call subDEFromHL - pop de ; Still have it? good - ; HL contains our number and we'll check its bounds. If It's negative, - ; H is going to be 0xff and L has to be >= 0x80. If it's positive, - ; H is going to be 0 and L has to be < 0x80. - ld a, l - cp 0x80 - jr c, .skipHInc ; a < 0x80, H is expected to be 0 - ; A being >= 0x80 is only valid in cases where HL is negative and - ; within bounds. This only happens is H == 0xff. Let's increase it to 0. - inc h -.skipHInc: - ; Let's write our value now even though we haven't checked our bounds - ; yet. This way, we don't have to store A somewhere else. - ld (de), a - ld a, h - or a ; cp 0 - jr nz, .numberTruncated ; if A is anything but zero, we're out - ; of bounds. - jr .writeIO - -.absoluteValue: - ; verify that the MSB in argument is zero - inc hl ; MSB is 2nd byte - ld a, (hl) - dec hl ; HL now points to LSB - or a ; cp 0 - jr nz, .numberTruncated - push bc - ldi - pop bc - inc c - jr .writeIO - -.withWord: - inc hl ; HL now points to LSB - ; Clear to proceed. HL already points to our number - push bc - ldi ; LSB written, we point to MSB now - ldi ; MSB written - pop bc - inc c ; two extra bytes are written - inc c - ; to writeIO -.writeIO: - ; Before we write IO, let's check a very specific case: is our first - ; upcode 0xcb and our byte count == 3? If yes, then swap the two last - ; bytes. In all instructions except 0xcb ones, IX/IY displacement comes - ; last, but in all 0xcb instructions, they come 2nd last. - call .checkCB - ; Let's write INS_UPCODE to IO - dec c \ inc c ; is C zero? - jr z, .numberTruncated - ld b, c ; save output byte count - ld hl, INS_UPCODE -.loopWrite: - ld a, (hl) - call ioPutB - jr nz, .ioError - inc hl - djnz .loopWrite - cp a ; ensure Z - jr .end -.numberTruncated: - ; Z already unset - ld a, ERR_OVFL - jr .end -.ioError: - ; Z already unset - ld a, SHELL_ERR_IO_ERROR - ; continue to .end -.end: - pop bc - pop hl - pop de - ret -.checkCB: - ld a, (INS_UPCODE) - cp 0xcb - ret nz - ld a, c - cp 3 - ret nz - ; We are in 0xcb + displacement situation. Swap bytes 2 and 3. - ld a, (INS_UPCODE+1) - ex af, af' - ld a, (INS_UPCODE+2) - ld (INS_UPCODE+1), a - ex af, af' - ld (INS_UPCODE+2), a - ret - -; Parse argument in (HL) and place it in (IX) -; Sets Z on success, reset on error. -processArg: - call parseArg - cp 0xff - jr z, .error - ld (ix), a - ; When A is a number, DE is set with the value of that number. Because - ; We don't use the space allocated to store those numbers in any other - ; occasion, we store DE there unconditonally, LSB first. - ld (ix+1), e - ld (ix+2), d - cp a ; ensure Z - ret -.error: - ld a, ERR_BAD_ARG - or a ; unset Z - ret - -; Parse instruction specified in A (I_* const) with args in I/O and write -; resulting opcode(s) in I/O. -; Sets Z on success. On error, A contains an error code (ERR_*) -parseInstruction: - push bc - push hl - push de - ; A is reused in .matchPrimaryRow but that register is way too changing. - ; Let's keep a copy in a more cosy register. - ld c, a - xor a - ld (INS_CURARG1), a - ld (INS_CURARG2), a - call readWord - jr nz, .nomorearg - ld ix, INS_CURARG1 - call processArg - jr nz, .end ; A is set to error, Z is unset - call readComma - jr nz, .nomorearg - call readWord - jr nz, .badfmt - ld ix, INS_CURARG2 - call processArg - jr nz, .end ; A is set to error, Z is unset -.nomorearg: - ; Parsing done, no error, let's move forward to instr row matching! - ; To speed up things a little, we use a poor man's indexing. Full - ; bisecting would involve too much complexity. - ld a, c ; recall A param - ld ix, instrTBl - cp I_EX - jr c, .loop - ld ix, instrTBlEX - cp I_LD - jr c, .loop - ld ix, instrTBlLD - cp I_RET - jr c, .loop - ld ix, instrTBlRET -.loop: - ld a, c ; recall A param - call .matchPrimaryRow - jr z, .match - ld de, INSTR_TBL_ROWSIZE - add ix, de - ld a, 0xff - cp (ix) - jr nz, .loop - ; No signature match - ld a, ERR_BAD_ARG - or a ; unset Z - jr .end -.match: - ; We have our matching instruction row. We're getting pretty near our - ; goal here! - call spitUpcode - jr .end ; Z and A set properly, even on error -.badfmt: - ; Z already unset - ld a, ERR_BAD_FMT -.end: - pop de - pop hl - pop bc - ret - -; Compare primary row at (IX) with ID in A. Sets Z flag if there's a match. -.matchPrimaryRow: - cp (ix) - ret nz - ; name matches, let's see the rest - ld hl, INS_CURARG1 - ld a, (ix+1) - call matchArg - ret nz - ld hl, INS_CURARG2 - ld a, (ix+2) - jp matchArg - - -; In instruction metadata below, argument types arge indicated with a single -; char mnemonic that is called "argspec". This is the table of correspondence. -; Single letters are represented by themselves, so we don't need as much -; metadata. -; Special meaning: -; 0 : no arg -; 1-10 : group id (see Groups section) -; 0xff: error - -; Format: 1 byte argspec + 4 chars string -argspecTbl: - .db 'A', "A", 0, 0, 0 - .db 'B', "B", 0, 0, 0 - .db 'C', "C", 0, 0, 0 - .db 'k', "(C)", 0 - .db 'D', "D", 0, 0, 0 - .db 'E', "E", 0, 0, 0 - .db 'H', "H", 0, 0, 0 - .db 'L', "L", 0, 0, 0 - .db 'I', "I", 0, 0, 0 - .db 'R', "R", 0, 0, 0 - .db 'h', "HL", 0, 0 - .db 'l', "(HL)" - .db 'd', "DE", 0, 0 - .db 'e', "(DE)" - .db 'b', "BC", 0, 0 - .db 'c', "(BC)" - .db 'a', "AF", 0, 0 - .db 'f', "AF'", 0 - .db 'X', "IX", 0, 0 - .db 'Y', "IY", 0, 0 - .db 'x', "(IX)" ; always come with displacement - .db 'y', "(IY)" ; with JP - .db 's', "SP", 0, 0 - .db 'p', "(SP)" -; we also need argspecs for the condition flags - .db 'Z', "Z", 0, 0, 0 - .db 'z', "NZ", 0, 0 - ; C is in conflict with the C register. The situation is ambiguous, but - ; doesn't cause actual problems. - .db '=', "NC", 0, 0 - .db '+', "P", 0, 0, 0 - .db '-', "M", 0, 0, 0 - .db '1', "PO", 0, 0 - .db '2', "PE", 0, 0 - -; argspecs not in the list: -; n -> N -; N -> NN -; m -> (N) (running out of mnemonics. 'm' for 'memory pointer') -; M -> (NN) - -; Groups -; Groups are specified by strings of argspecs. To facilitate jumping to them, -; we have a fixed-sized table. Because most of them are 2 or 4 bytes long, we -; have a table that is 4 in size to minimize consumed space. We treat the two -; groups that take 8 bytes in a special way. -; -; The table below is in order, starting with group 0x01 -argGrpTbl: - .db "bdha" ; 0x01 - .db "ZzC=" ; 0x02 - .db "bdhs" ; 0x03 - .db "bdXs" ; 0x04 - .db "bdYs" ; 0x05 - -argGrpCC: - .db "zZ=C12+-" ; 0xa -argGrpABCDEHL: - .db "BCDEHL_A" ; 0xb - -; SPECIAL GROUP "BIT": 0xc -; When special group "0xc" shows up in argspec, it means: accept a number -; between 0 and 7. The value is then treated like a regular group value. - -; Each row is 4 bytes wide, fill with zeroes -instrNames: - .db "ADC", 0 - .db "ADD", 0 - .db "AND", 0 - .db "BIT", 0 - .db "CALL" - .db "CCF", 0 - .db "CP",0,0 - .db "CPD", 0 - .db "CPDR" - .db "CPI", 0 - .db "CPIR" - .db "CPL", 0 - .db "DAA", 0 - .db "DEC", 0 - .db "DI",0,0 - .db "DJNZ" - .db "EI",0,0 - .db "EX",0,0 - .db "EXX", 0 - .db "HALT" - .db "IM",0,0 - .db "IN",0,0 - .db "INC", 0 - .db "IND", 0 - .db "INDR" - .db "INI", 0 - .db "INIR" - .db "JP",0,0 - .db "JR",0,0 - .db "LD",0,0 - .db "LDD", 0 - .db "LDDR" - .db "LDI", 0 - .db "LDIR" - .db "NEG", 0 - .db "NOP", 0 - .db "OR",0,0 - .db "OTDR" - .db "OTIR" - .db "OUT", 0 - .db "POP", 0 - .db "PUSH" - .db "RES", 0 - .db "RET", 0 - .db "RETI" - .db "RETN" - .db "RL", 0, 0 - .db "RLA", 0 - .db "RLC", 0 - .db "RLCA" - .db "RR", 0, 0 - .db "RRA", 0 - .db "RRC", 0 - .db "RRCA" - .db "RST", 0 - .db "SBC", 0 - .db "SCF", 0 - .db "SET", 0 - .db "SLA", 0 - .db "SRL", 0 - .db "SUB", 0 - .db "XOR", 0 - -; This is a list of all supported instructions. Each row represent a combination -; of instr/argspecs (which means more than one row per instr). Format: -; -; 1 byte for the instruction ID -; 1 byte for arg constant -; 1 byte for 2nd arg constant -; 1 byte displacement for group arguments + flags -; 2 bytes for upcode (2nd byte is zero if instr is one byte) -; -; An "arg constant" is a char corresponding to either a row in argspecTbl or -; a group index in argGrpTbl (values < 0x10 are considered group indexes). -; -; The displacement bit is split in 2 nibbles: lower nibble is the displacement -; value, upper nibble is for flags: -; -; Bit 7: indicates that the numerical argument is of the 'e' type and has to be -; decreased by 2 (djnz, jr). -; Bit 6: it indicates that the group argument's value is to be placed on the -; second upcode rather than the first. -; Bit 5: Indicates that this row is handled very specially: the next two bytes -; aren't upcode bytes, but a routine address to call to handle this case with -; custom code. -; Bit 4: When in an 'l' argspec, this means "I accept IX and IY variants". - -; This table needs to be kept in ascending order of I_* value. -instrTBl: - .db I_ADC, 'A', 'l', 0, 0x8e , 0 ; ADC A, (HL) - .db I_ADC, 'A', 0xb, 0, 0b10001000 , 0 ; ADC A, r - .db I_ADC, 'A', 'n', 0, 0xce , 0 ; ADC A, n - .db I_ADC, 'h', 0x3, 0x44, 0xed, 0b01001010 ; ADC HL, ss - .db I_ADD, 'A', 'l', 0x10, 0x86 , 0 ; ADD A, (HL) + (IX/Y) - .db I_ADD, 'A', 0xb, 0, 0b10000000 , 0 ; ADD A, r - .db I_ADD, 'A', 'n', 0, 0xc6 , 0 ; ADD A, n - .db I_ADD, 'h', 0x3, 4, 0b00001001 , 0 ; ADD HL, ss - .db I_ADD, 'X', 0x4, 0x44, 0xdd, 0b00001001 ; ADD IX, pp - .db I_ADD, 'Y', 0x5, 0x44, 0xfd, 0b00001001 ; ADD IY, rr - .db I_AND, 'l', 0, 0x10, 0xa6 , 0 ; AND (HL) + (IX/Y) - .db I_AND, 0xb, 0, 0, 0b10100000 , 0 ; AND r - .db I_AND, 'n', 0, 0, 0xe6 , 0 ; AND n - .db I_BIT, 0xc, 'l', 0x53, 0xcb, 0b01000110 ; BIT b, (HL) + (IX/Y) - .db I_BIT, 0xc, 0xb, 0x20 \ .dw handleBITR ; BIT b, r - .db I_CALL,0xa, 'N', 3, 0b11000100 , 0 ; CALL cc, NN - .db I_CALL,'N', 0, 0, 0xcd , 0 ; CALL NN - .db I_CCF, 0, 0, 0, 0x3f , 0 ; CCF - .db I_CP, 'l', 0, 0x10, 0xbe , 0 ; CP (HL) + (IX/Y) - .db I_CP, 0xb, 0, 0, 0b10111000 , 0 ; CP r - .db I_CP, 'n', 0, 0, 0xfe , 0 ; CP n - .db I_CPD, 0, 0, 0, 0xed, 0xa9 ; CPD - .db I_CPDR,0, 0, 0, 0xed, 0xb9 ; CPDR - .db I_CPI, 0, 0, 0, 0xed, 0xa1 ; CPI - .db I_CPIR,0, 0, 0, 0xed, 0xb1 ; CPIR - .db I_CPL, 0, 0, 0, 0x2f , 0 ; CPL - .db I_DAA, 0, 0, 0, 0x27 , 0 ; DAA - .db I_DEC, 'l', 0, 0x10, 0x35 , 0 ; DEC (HL) + (IX/Y) - .db I_DEC, 'X', 0, 0, 0xdd, 0x2b ; DEC IX - .db I_DEC, 'Y', 0, 0, 0xfd, 0x2b ; DEC IY - .db I_DEC, 0xb, 0, 3, 0b00000101 , 0 ; DEC r - .db I_DEC, 0x3, 0, 4, 0b00001011 , 0 ; DEC ss - .db I_DI, 0, 0, 0, 0xf3 , 0 ; DI - .db I_DJNZ,'n', 0, 0x80, 0x10 , 0 ; DJNZ e - .db I_EI, 0, 0, 0, 0xfb , 0 ; EI -instrTBlEX: - .db I_EX, 'p', 'h', 0, 0xe3 , 0 ; EX (SP), HL - .db I_EX, 'p', 'X', 0, 0xdd, 0xe3 ; EX (SP), IX - .db I_EX, 'p', 'Y', 0, 0xfd, 0xe3 ; EX (SP), IY - .db I_EX, 'a', 'f', 0, 0x08 , 0 ; EX AF, AF' - .db I_EX, 'd', 'h', 0, 0xeb , 0 ; EX DE, HL - .db I_EXX, 0, 0, 0, 0xd9 , 0 ; EXX - .db I_HALT,0, 0, 0, 0x76 , 0 ; HALT - .db I_IM, 'n', 0, 0x20 \ .dw handleIM ; IM {0,1,2} - .db I_IN, 'A', 'm', 0, 0xdb , 0 ; IN A, (n) - .db I_IN, 0xb, 'k', 0x43, 0xed, 0b01000000 ; IN r, (C) - .db I_INC, 'l', 0, 0x10, 0x34 , 0 ; INC (HL) + (IX/Y) - .db I_INC, 'X', 0, 0, 0xdd , 0x23 ; INC IX - .db I_INC, 'Y', 0, 0, 0xfd , 0x23 ; INC IY - .db I_INC, 0xb, 0, 3, 0b00000100 , 0 ; INC r - .db I_INC, 0x3, 0, 4, 0b00000011 , 0 ; INC ss - .db I_IND, 0, 0, 0, 0xed, 0xaa ; IND - .db I_INDR,0, 0, 0, 0xed, 0xba ; INDR - .db I_INI, 0, 0, 0, 0xed, 0xa2 ; INI - .db I_INIR,0, 0, 0, 0xed, 0xb2 ; INIR - .db I_JP, 'x', 0, 0x20 \ .dw handleJPIXY ; JP (IX) - .db I_JP, 'y', 0, 0x20 \ .dw handleJPIXY ; JP (IY) - .db I_JP, 'l', 0, 0, 0xe9 , 0 ; JP (HL) - .db I_JP, 0xa, 'N', 3, 0b11000010 , 0 ; JP cc, NN - .db I_JP, 'N', 0, 0, 0xc3 , 0 ; JP NN - .db I_JR, 'n', 0, 0x80, 0x18 , 0 ; JR e - .db I_JR, 'C', 'n', 0x80, 0x38 , 0 ; JR C, e - .db I_JR, '=', 'n', 0x80, 0x30 , 0 ; JR NC, e - .db I_JR, 'Z', 'n', 0x80, 0x28 , 0 ; JR Z, e - .db I_JR, 'z', 'n', 0x80, 0x20 , 0 ; JR NZ, e -instrTBlLD: - .db I_LD, 'c', 'A', 0, 0x02 , 0 ; LD (BC), A - .db I_LD, 'e', 'A', 0, 0x12 , 0 ; LD (DE), A - .db I_LD, 'A', 'c', 0, 0x0a , 0 ; LD A, (BC) - .db I_LD, 'A', 'e', 0, 0x1a , 0 ; LD A, (DE) - .db I_LD, 's', 'h', 0, 0xf9 , 0 ; LD SP, HL - .db I_LD, 'A', 'I', 0, 0xed, 0x57 ; LD A, I - .db I_LD, 'I', 'A', 0, 0xed, 0x47 ; LD I, A - .db I_LD, 'A', 'R', 0, 0xed, 0x5f ; LD A, R - .db I_LD, 'R', 'A', 0, 0xed, 0x4f ; LD R, A - .db I_LD, 'l', 0xb, 0, 0b01110000 , 0 ; LD (HL), r - .db I_LD, 0xb, 'l', 3, 0b01000110 , 0 ; LD r, (HL) - .db I_LD, 'l', 'n', 0, 0x36 , 0 ; LD (HL), n - .db I_LD, 0xb, 'n', 3, 0b00000110 , 0 ; LD r, n - .db I_LD, 0xb, 0xb, 0x20 \ .dw handleLDrr ; LD r, r' - .db I_LD, 0x3, 'N', 4, 0b00000001 , 0 ; LD dd, nn - .db I_LD, 'X', 'N', 0, 0xdd, 0x21 ; LD IX, NN - .db I_LD, 'Y', 'N', 0, 0xfd, 0x21 ; LD IY, NN - .db I_LD, 'M', 'A', 0, 0x32 , 0 ; LD (NN), A - .db I_LD, 'A', 'M', 0, 0x3a , 0 ; LD A, (NN) - .db I_LD, 'M', 'h', 0, 0x22 , 0 ; LD (NN), HL - .db I_LD, 'h', 'M', 0, 0x2a , 0 ; LD HL, (NN) - .db I_LD, 'M', 'X', 0, 0xdd, 0x22 ; LD (NN), IX - .db I_LD, 'X', 'M', 0, 0xdd, 0x2a ; LD IX, (NN) - .db I_LD, 'M', 'Y', 0, 0xfd, 0x22 ; LD (NN), IY - .db I_LD, 'Y', 'M', 0, 0xfd, 0x2a ; LD IY, (NN) - .db I_LD, 'M', 0x3, 0x44, 0xed, 0b01000011 ; LD (NN), dd - .db I_LD, 0x3, 'M', 0x44, 0xed, 0b01001011 ; LD dd, (NN) - .db I_LD, 'x', 'n', 0x20 \ .dw handleLDIXYn ; LD (IX+d), n - .db I_LD, 'y', 'n', 0x20 \ .dw handleLDIXYn ; LD (IY+d), n - .db I_LD, 'x', 0xb, 0x20 \ .dw handleLDIXYr ; LD (IX+d), r - .db I_LD, 'y', 0xb, 0x20 \ .dw handleLDIXYr ; LD (IY+d), r - .db I_LD, 0xb, 'x', 0x20 \ .dw handleLDrIXY ; LD r, (IX+d) - .db I_LD, 0xb, 'y', 0x20 \ .dw handleLDrIXY ; LD r, (IY+d) - .db I_LDD, 0, 0, 0, 0xed, 0xa8 ; LDD - .db I_LDDR,0, 0, 0, 0xed, 0xb8 ; LDDR - .db I_LDI, 0, 0, 0, 0xed, 0xa0 ; LDI - .db I_LDIR,0, 0, 0, 0xed, 0xb0 ; LDIR - .db I_NEG, 0, 0, 0, 0xed, 0x44 ; NEG - .db I_NOP, 0, 0, 0, 0x00 , 0 ; NOP - .db I_OR, 'l', 0, 0x10, 0xb6 , 0 ; OR (HL) + (IX/Y) - .db I_OR, 0xb, 0, 0, 0b10110000 , 0 ; OR r - .db I_OR, 'n', 0, 0, 0xf6 , 0 ; OR n - .db I_OTDR,0, 0, 0, 0xed, 0xbb ; OTDR - .db I_OTIR,0, 0, 0, 0xed, 0xb3 ; OTIR - .db I_OUT, 'm', 'A', 0, 0xd3 , 0 ; OUT (n), A - .db I_OUT, 'k', 0xb, 0x43, 0xed, 0b01000001 ; OUT (C), r - .db I_POP, 'X', 0, 0, 0xdd, 0xe1 ; POP IX - .db I_POP, 'Y', 0, 0, 0xfd, 0xe1 ; POP IY - .db I_POP, 0x1, 0, 4, 0b11000001 , 0 ; POP qq - .db I_PUSH,'X', 0, 0, 0xdd, 0xe5 ; PUSH IX - .db I_PUSH,'Y', 0, 0, 0xfd, 0xe5 ; PUSH IY - .db I_PUSH,0x1, 0, 4, 0b11000101 , 0 ; PUSH qq - .db I_RES, 0xc, 'l', 0x53, 0xcb, 0b10000110 ; RES b, (HL) + (IX/Y) - .db I_RES, 0xc, 0xb, 0x20 \ .dw handleRESR ; RES b, r -instrTBlRET: - .db I_RET, 0, 0, 0, 0xc9 , 0 ; RET - .db I_RET, 0xa, 0, 3, 0b11000000 , 0 ; RET cc - .db I_RETI,0, 0, 0, 0xed, 0x4d ; RETI - .db I_RETN,0, 0, 0, 0xed, 0x45 ; RETN - .db I_RL, 0xb, 0,0x40, 0xcb, 0b00010000 ; RL r - .db I_RL, 'l', 0,0x10, 0xcb, 0b00010110 ; RL (HL) + (IX/Y) - .db I_RLA, 0, 0, 0, 0x17 , 0 ; RLA - .db I_RLC, 0xb, 0,0x40, 0xcb, 0b00000000 ; RLC r - .db I_RLCA,0, 0, 0, 0x07 , 0 ; RLCA - .db I_RR, 0xb, 0,0x40, 0xcb, 0b00011000 ; RR r - .db I_RR, 'l', 0,0x10, 0xcb, 0b00011110 ; RR (HL) + (IX/Y) - .db I_RRA, 0, 0, 0, 0x1f , 0 ; RRA - .db I_RRC, 0xb, 0,0x40, 0xcb, 0b00001000 ; RRC r - .db I_RRCA,0, 0, 0, 0x0f , 0 ; RRCA - .db I_RST, 'n', 0, 0x20 \ .dw handleRST ; RST p - .db I_SBC, 'A', 'l', 0, 0x9e , 0 ; SBC A, (HL) - .db I_SBC, 'A', 0xb, 0, 0b10011000 , 0 ; SBC A, r - .db I_SBC,'h',0x3,0x44, 0xed, 0b01000010 ; SBC HL, ss - .db I_SCF, 0, 0, 0, 0x37 , 0 ; SCF - .db I_SET, 0xc, 'l', 0x53, 0xcb, 0b11000110 ; SET b, (HL) + (IX/Y) - .db I_SET, 0xc, 0xb, 0x20 \ .dw handleSETR ; SET b, r - .db I_SLA, 0xb, 0,0x40, 0xcb, 0b00100000 ; SLA r - .db I_SRL, 0xb, 0,0x40, 0xcb, 0b00111000 ; SRL r - .db I_SRL, 'l', 0,0x10, 0xcb, 0b00111110 ; SRL (HL) + (IX/Y) - .db I_SUB, 'l', 0, 0, 0x96 , 0 ; SUB (HL) - .db I_SUB, 0xb, 0, 0, 0b10010000 , 0 ; SUB r - .db I_SUB, 'n', 0, 0, 0xd6 , 0 ; SUB n - .db I_XOR, 'l', 0, 0, 0xae , 0 ; XOR (HL) - .db I_XOR, 0xb, 0, 0, 0b10101000 , 0 ; XOR r - .db I_XOR, 'n', 0, 0, 0xee , 0 ; XOR n - .db 0xff diff --git a/apps/zasm/io.asm b/apps/zasm/io.asm deleted file mode 100644 index 6bd9991..0000000 --- a/apps/zasm/io.asm +++ /dev/null @@ -1,291 +0,0 @@ -; I/Os in zasm -; -; As a general rule, I/O in zasm is pretty straightforward. We receive, as a -; parameter, two blockdevs: One that we can read and seek and one that we can -; write to (we never seek into it). -; -; This unit also has the responsibility of counting the number of written bytes, -; maintaining IO_PC and of properly disabling output on first pass. -; -; On top of that, this unit has the responsibility of keeping track of the -; current lineno. Whenever GetB is called, we check if the fetched byte is a -; newline. If it is, we increase our lineno. This unit is the best place to -; keep track of this because we have to handle ioRecallPos. -; -; zasm doesn't buffers its reads during tokenization, which simplifies its -; process. However, it also means that it needs, in certain cases, a "putback" -; mechanism, that is, a way to say "you see that character I've just read? that -; was out of my bounds. Could you make it as if I had never read it?". That -; buffer is one character big and is made with the expectation that ioPutBack -; is always called right after a ioGetB (when it's called). -; -; ioPutBack will mess up seek and tell offsets, so thath "put back" should be -; consumed before having to seek and tell. -; -; That's for the general rules. -; -; Now, let's enter includes. To simplify processing, we make include mostly -; transparent to all other units. They always read from ioGetB and a include -; directive should have the exact same effect as copy/pasting the contents of -; the included file in the caller. -; -; By the way: we don't support multiple level of inclusion. Only top level files -; can include. -; -; When we include, all we do here is open the file with fsOpen and set a flag -; indicating that we're inside an include. When that flag is on, GetB, Seek and -; Tell are transparently redirected to their fs* counterpart. -; -; When we reach EOF in an included file, we transparently unset the "in include" -; flag and continue on the general IN stream. - -; *** Variables *** -.equ IO_IN_BLK IO_RAMSTART -.equ IO_OUT_BLK IO_IN_BLK+BLOCKDEV_SIZE -; Save pos for ioSavePos and ioRecallPos -.equ IO_SAVED_POS IO_OUT_BLK+BLOCKDEV_SIZE -; File handle for included source -.equ IO_INCLUDE_HDL IO_SAVED_POS+2 -; blkdev for include file -.equ IO_INCLUDE_BLK IO_INCLUDE_HDL+FS_HANDLE_SIZE -; see ioPutBack below -.equ IO_PUTBACK_BUF IO_INCLUDE_BLK+BLOCKDEV_SIZE -.equ IO_IN_INCLUDE IO_PUTBACK_BUF+1 -.equ IO_PC IO_IN_INCLUDE+1 -; Current lineno in top-level file -.equ IO_LINENO IO_PC+2 -; Current lineno in include file -.equ IO_INC_LINENO IO_LINENO+2 -; Line number (can be top-level or include) when ioSavePos was last called. -.equ IO_SAVED_LINENO IO_INC_LINENO+2 -; Handle for the ioSpitBin -.equ IO_BIN_HDL IO_SAVED_LINENO+2 -.equ IO_RAMEND IO_BIN_HDL+FS_HANDLE_SIZE - -; *** Code *** - -ioInit: - xor a - ld (IO_PUTBACK_BUF), a - ld (IO_IN_INCLUDE), a - ld de, IO_INCLUDE_BLK - ld hl, _ioIncBlk - call blkSet - jp ioResetCounters - -ioGetB: - ld a, (IO_PUTBACK_BUF) - or a ; cp 0 - jr nz, .getback - call ioInInclude - jr z, .normalmode - ; We're in "include mode", read from FS - push ix ; --> lvl 1 - ld ix, IO_INCLUDE_BLK - call _blkGetB - pop ix ; <-- lvl 1 - jr nz, .includeEOF - cp 0x0a ; newline - ret nz ; not newline? nothing to do - ; We have newline. Increase lineno and return (the rest of the - ; processing below isn't needed. - push hl - ld hl, (IO_INC_LINENO) - inc hl - ld (IO_INC_LINENO), hl - pop hl - ret - -.includeEOF: - ; We reached EOF. What we do depends on whether we're in Local Pass - ; mode. Yes, I know, a bit hackish. Normally, we *should* be - ; transparently getting of include mode and avoid meddling with global - ; states, but here, we need to tell main.asm that the local scope if - ; over *before* we get off include mode, otherwise, our IO_SAVED_POS - ; will be wrong (an include IO_SAVED_POS used in global IN stream). - call zasmIsLocalPass - ld a, 0 ; doesn't affect Z flag - ret z ; local pass? return EOF - ; regular pass (first or second)? transparently get off include mode. - ld (IO_IN_INCLUDE), a ; A already 0 - ld (IO_INC_LINENO), a - ld (IO_INC_LINENO+1), a - ; continue on to "normal" reading. We don't want to return our zero -.normalmode: - ; normal mode, read from IN stream - push ix ; --> lvl 1 - ld ix, IO_IN_BLK - call _blkGetB - pop ix ; <-- lvl 1 - cp LF ; newline - ret nz ; not newline? return - ; inc current lineno - push hl - ld hl, IO_LINENO - inc (hl) - pop hl - cp a ; ensure Z - ret - -.getback: - push af - xor a - ld (IO_PUTBACK_BUF), a - pop af - ret - -; Put back non-zero character A into the "ioGetB stack". The next ioGetB call, -; instead of reading from IO_IN_BLK, will return that character. That's the -; easiest way I found to handle the readWord/gotoNextLine problem. -ioPutBack: - ld (IO_PUTBACK_BUF), a - ret - -ioPutB: - push hl ; --> lvl 1 - ld hl, (IO_PC) - inc hl - ld (IO_PC), hl - pop hl ; <-- lvl 1 - push af ; --> lvl 1 - call zasmIsFirstPass - jr z, .skip - pop af ; <-- lvl 1 - push ix ; --> lvl 1 - ld ix, IO_OUT_BLK - call _blkPutB - pop ix ; <-- lvl 1 - ret -.skip: - pop af ; <-- lvl 1 - cp a ; ensure Z - ret - -ioSavePos: - ld hl, (IO_LINENO) - call ioInInclude - jr z, .skip - ld hl, (IO_INC_LINENO) -.skip: - ld (IO_SAVED_LINENO), hl - call _ioTell - ld (IO_SAVED_POS), hl - ret - -ioRecallPos: - ld hl, (IO_SAVED_LINENO) - call ioInInclude - jr nz, .include - ld (IO_LINENO), hl - jr .recallpos -.include: - ld (IO_INC_LINENO), hl -.recallpos: - ld hl, (IO_SAVED_POS) - jr _ioSeek - -ioRewind: - call ioResetCounters ; sets HL to 0 - jr _ioSeek - -ioResetCounters: - ld hl, 0 - ld (IO_PC), hl - ld (IO_LINENO), hl - ld (IO_SAVED_LINENO), hl - ret - -; always in absolute mode (A = 0) -_ioSeek: - call ioInInclude - ld a, 0 ; don't alter flags - jr nz, .include - ; normal mode, seek in IN stream - ld ix, IO_IN_BLK - jp _blkSeek -.include: - ; We're in "include mode", seek in FS - ld ix, IO_INCLUDE_BLK - jp _blkSeek ; returns - -_ioTell: - call ioInInclude - jp nz, .include - ; normal mode, seek in IN stream - ld ix, IO_IN_BLK - jp _blkTell -.include: - ; We're in "include mode", tell from FS - ld ix, IO_INCLUDE_BLK - jp _blkTell ; returns - -; Sets Z according to whether we're inside an include -; Z is set when we're *not* in includes. A bit weird, I know... -ioInInclude: - ld a, (IO_IN_INCLUDE) - or a ; cp 0 - ret - -; Open include file name specified in (HL). -; Sets Z on success, unset on error. -ioOpenInclude: - call ioPrintLN - call fsFindFN - ret nz - ld ix, IO_INCLUDE_HDL - call fsOpen - ld a, 1 - ld (IO_IN_INCLUDE), a - ld hl, 0 - ld (IO_INC_LINENO), hl - xor a - ld ix, IO_INCLUDE_BLK - call _blkSeek - cp a ; ensure Z - ret - -; Open file specified in (HL) and spit its contents through ioPutB -; Sets Z on success. -ioSpitBin: - call fsFindFN - ret nz - push hl ; --> lvl 1 - ld ix, IO_BIN_HDL - call fsOpen - ld hl, 0 -.loop: - ld ix, IO_BIN_HDL - call fsGetB - jr nz, .loopend - call ioPutB - inc hl - jr .loop -.loopend: - pop hl ; <-- lvl 1 - cp a ; ensure Z - ret - -; Return current lineno in HL and, if in an include, its lineno in DE. -; If not in an include, DE is set to 0 -ioLineNo: - push af - ld hl, (IO_LINENO) - ld de, 0 - call ioInInclude - jr z, .end - ld de, (IO_INC_LINENO) -.end: - pop af - ret - -_ioIncGetB: - ld ix, IO_INCLUDE_HDL - jp fsGetB - -_ioIncBlk: - .dw _ioIncGetB, unsetZ - -; call printstr followed by newline -ioPrintLN: - call printstr - jp printcrlf diff --git a/apps/zasm/main.asm b/apps/zasm/main.asm deleted file mode 100644 index 59e3f31..0000000 --- a/apps/zasm/main.asm +++ /dev/null @@ -1,245 +0,0 @@ -; *** Variables *** - -; A bool flag indicating that we're on first pass. When we are, we don't care -; about actual output, but only about the length of each upcode. This means -; that when we parse instructions and directive that error out because of a -; missing symbol, we don't error out and just write down a dummy value. -.equ ZASM_FIRST_PASS ZASM_RAMSTART -; whether we're in "local pass", that is, in local label scanning mode. During -; this special pass, ZASM_FIRST_PASS will also be set so that the rest of the -; code behaves as is we were in the first pass. -.equ ZASM_LOCAL_PASS @+1 -; What IO_PC was when we started our context -.equ ZASM_CTX_PC @+1 -; current ".org" offset, that is, what we must offset all our label by. -.equ ZASM_ORG @+2 -.equ ZASM_RAMEND @+2 - -; Takes 2 byte arguments, blkdev in and blkdev out, expressed as IDs. -; Can optionally take a 3rd argument which is the high byte of the initial -; .org. For example, passing 0x42 to this 3rd arg is the equivalent of beginning -; the unit with ".org 0x4200". -; Read file through blkdev in and outputs its upcodes through blkdev out. -; HL is set to the last lineno to be read. -; Sets Z on success, unset on error. On error, A contains an error code (ERR_*) -zasmMain: - ; Parse args in (HL) - ; blkdev in - call parseHexadecimal ; --> DE - jr nz, .badargs - ld a, e - ld de, IO_IN_BLK - call blkSel - - ; blkdev in - call rdWS - jr nz, .badargs - call parseHexadecimal ; --> DE - jr nz, .badargs - ld a, e - ld de, IO_OUT_BLK - call blkSel - - ; .org high byte - ld e, 0 ; in case we .skipOrgSet - call rdWS - jr nz, .skipOrgSet ; no org argument - call parseHexadecimal ; --> DE - jr nz, .badargs - -.skipOrgSet: - ; Init .org with value of E - ; Save in "@" too - ld a, e - ld (ZASM_ORG+1), a ; high byte of .org - ld (DIREC_LASTVAL+1), a - xor a - ld (ZASM_ORG), a ; low byte zero in all cases - ld (DIREC_LASTVAL), a - - ; And then the rest. - ld (ZASM_LOCAL_PASS), a - call ioInit - call symInit - - ; First pass - ld hl, .sFirstPass - call ioPrintLN - ld a, 1 - ld (ZASM_FIRST_PASS), a - call zasmParseFile - jr nz, .end - ; Second pass - ld hl, .sSecondPass - call ioPrintLN - xor a - ld (ZASM_FIRST_PASS), a - ; before parsing the file for the second pass, let's clear the const - ; registry. See comment in handleEQU. - ld ix, SYM_CONST_REGISTRY - call symClear - call zasmParseFile -.end: - jp ioLineNo ; --> HL, --> DE, returns - -.badargs: - ; bad args - ld a, SHELL_ERR_BAD_ARGS - ret - -.sFirstPass: - .db "First pass", 0 -.sSecondPass: - .db "Second pass", 0 - -; Sets Z according to whether we're in first pass. -zasmIsFirstPass: - ld a, (ZASM_FIRST_PASS) - cp 1 - ret - -; Sets Z according to whether we're in local pass. -zasmIsLocalPass: - ld a, (ZASM_LOCAL_PASS) - cp 1 - ret - -; Set ZASM_ORG to specified number in HL -zasmSetOrg: - ld (ZASM_ORG), hl - ret - -; Return current PC (properly .org offsetted) in HL -zasmGetPC: - push de - ld hl, (ZASM_ORG) - ld de, (IO_PC) - add hl, de - pop de - ret - -; Repeatedly reads lines from IO, assemble them and spit the binary code in -; IO. Z is set on success, unset on error. DE contains the last line number to -; be read (first line is 1). -zasmParseFile: - call ioRewind -.loop: - call parseLine - ret nz ; error - ld a, b ; TOK_* - cp TOK_EOF - jr z, .eof - jr .loop -.eof: - call zasmIsLocalPass - jr nz, .end ; EOF and not local pass - ; we're in local pass and EOF. Unwind this - call _endLocalPass - jr .loop -.end: - cp a ; ensure Z - ret - -; Parse next token and accompanying args (when relevant) in I/O, write the -; resulting opcode(s) through ioPutB and increases (IO_PC) by the number of -; bytes written. BC is set to the result of the call to tokenize. -; Sets Z if parse was successful, unset if there was an error. EOF is not an -; error. If there is an error, A is set to the corresponding error code (ERR_*). -parseLine: - call tokenize - ld a, b ; TOK_* - cp TOK_INSTR - jp z, _parseInstr - cp TOK_DIRECTIVE - jp z, _parseDirec - cp TOK_LABEL - jr z, _parseLabel - cp TOK_EOF - ret z ; We're finished, no error. - ; Bad token - ld a, ERR_UNKNOWN - jp unsetZ ; return with Z unset - -_parseInstr: - ld a, c ; I_* - jp parseInstruction - -_parseDirec: - ld a, c ; D_* - jp parseDirective - -_parseLabel: - ; The string in (scratchpad) is a label with its trailing ':' removed. - ld hl, scratchpad - - call zasmIsLocalPass - jr z, .processLocalPass - - ; Is this a local label? If yes, we don't process it in the context of - ; parseLine, whether it's first or second pass. Local labels are only - ; parsed during the Local Pass - call symIsLabelLocal - jr z, .success ; local? don't do anything. - - ld ix, SYM_GLOBAL_REGISTRY - call zasmIsFirstPass - jr z, .registerLabel ; When we encounter a label in the first - ; pass, we register it in the symbol - ; list - ; At this point, we're in second pass, we've encountered a global label - ; and we'll soon continue processing our file. However, before we do - ; that, we should process our local labels. - call _beginLocalPass - jr .success -.processLocalPass: - ld ix, SYM_LOCAL_REGISTRY - call symIsLabelLocal - jr z, .registerLabel ; local label? all good, register it - ; normally - ; not a local label? Then we need to end local pass - call _endLocalPass - jr .success -.registerLabel: - push hl - call zasmGetPC - ex de, hl - pop hl - call symRegister - jr nz, .error - ; continue to .success -.success: - xor a ; ensure Z - ret -.error: - call unsetZ - ret - -_beginLocalPass: - ; remember were I/O was - call ioSavePos - ; Remember where PC was - ld hl, (IO_PC) - ld (ZASM_CTX_PC), hl - ; Fake first pass - ld a, 1 - ld (ZASM_FIRST_PASS), a - ; Set local pass - ld (ZASM_LOCAL_PASS), a - ; Empty local label registry - ld ix, SYM_LOCAL_REGISTRY - jp symClear - - -_endLocalPass: - ; recall I/O pos - call ioRecallPos - ; recall PC - ld hl, (ZASM_CTX_PC) - ld (IO_PC), hl - ; unfake first pass - xor a - ld (ZASM_FIRST_PASS), a - ; Unset local pass - ld (ZASM_LOCAL_PASS), a - cp a ; ensure Z - ret diff --git a/apps/zasm/parse.asm b/apps/zasm/parse.asm deleted file mode 100644 index de32513..0000000 --- a/apps/zasm/parse.asm +++ /dev/null @@ -1,45 +0,0 @@ -; Parse string in (HL) and return its numerical value whether its a number -; literal or a symbol. Returns value in DE. -; HL is advanced to the character following the last successfully read char. -; Sets Z if number or symbol is valid, unset otherwise. -parseNumberOrSymbol: - call isLiteralPrefix - jp z, parseLiteral - ; Not a number. try symbol - ld a, (hl) - cp '$' - jr z, .PC - cp '@' - jr z, .lastVal - call symParse - ret nz - ; HL at end of symbol name, DE at tmp null-terminated symname. - push hl ; --> lvl 1 - ex de, hl - call symFindVal ; --> DE - pop hl ; <-- lvl 1 - ret z - ; not found - ; When not found, check if we're in first pass. If we are, it doesn't - ; matter that we didn't find our symbol. Return success anyhow. - ; Otherwise return error. Z is already unset, so in fact, this is the - ; same as jumping to zasmIsFirstPass - ; however, before we do, load DE with zero. Returning dummy non-zero - ; values can have weird consequence (such as false overflow errors). - ld de, 0 - jp zasmIsFirstPass - -.PC: - ex de, hl - call zasmGetPC ; --> HL - ex de, hl ; result in DE - inc hl ; char after last read - ; Z already set from cp '$' - ret - -.lastVal: - ; last val - ld de, (DIREC_LASTVAL) - inc hl ; char after last read - ; Z already set from cp '@' - ret diff --git a/apps/zasm/symbol.asm b/apps/zasm/symbol.asm deleted file mode 100644 index a1091c9..0000000 --- a/apps/zasm/symbol.asm +++ /dev/null @@ -1,340 +0,0 @@ -; Manages both constants and labels within a same namespace and registry. -; -; Local Labels -; -; Local labels during the "official" first pass are ignored. To register them -; in the global registry during that pass would be wasteful in terms of memory. -; -; What we do instead is set up a separate register for them and have a "second -; first pass" whenever we encounter a new context. That is, we wipe the local -; registry, parse the code until the next global symbol (or EOF), then rewind -; and continue second pass as usual. -; -; What is a symbol name? The accepted characters for a symbol are A-Z, a-z, 0-9 -; dot (.) and underscore (_). -; This unit doesn't disallow symbols starting with a digit, but in effect, they -; aren't going to work because parseLiteral is going to get that digit first. -; So, make your symbols start with a letter or dot or underscore. - -; *** Constants *** -; Size of each record in registry -.equ SYM_RECSIZE 3 - -.equ SYM_REGSIZE ZASM_REG_BUFSZ+1+ZASM_REG_MAXCNT*SYM_RECSIZE - -.equ SYM_LOC_REGSIZE ZASM_LREG_BUFSZ+1+ZASM_LREG_MAXCNT*SYM_RECSIZE - -; Maximum name length for a symbol -.equ SYM_NAME_MAXLEN 0x20 - -; *** Variables *** -; A registry has three parts: record count (byte) record list and names pool. -; A record is a 3 bytes structure: -; 1b - name length -; 2b - value associated to symbol -; -; We know we're at the end of the record list when we hit a 0-length one. -; -; The names pool is a list of strings, not null-terminated, associated with -; the value. -; -; It is assumed that the registry is aligned in memory in that order: -; names pool, rec count, reclist - -; Global labels registry -.equ SYM_GLOB_REG SYM_RAMSTART -.equ SYM_LOC_REG @+SYM_REGSIZE -.equ SYM_CONST_REG @+SYM_LOC_REGSIZE -; Area where we parse symbol names into -.equ SYM_TMPNAME @+SYM_REGSIZE -.equ SYM_RAMEND @+SYM_NAME_MAXLEN+1 - -; *** Registries *** -; A symbol registry is a 5 bytes record with points to the name pool then the -; records list of the register and then the max record count. - -SYM_GLOBAL_REGISTRY: - .dw SYM_GLOB_REG, SYM_GLOB_REG+ZASM_REG_BUFSZ - .db ZASM_REG_MAXCNT - -SYM_LOCAL_REGISTRY: - .dw SYM_LOC_REG, SYM_LOC_REG+ZASM_LREG_BUFSZ - .db ZASM_LREG_MAXCNT - -SYM_CONST_REGISTRY: - .dw SYM_CONST_REG, SYM_CONST_REG+ZASM_REG_BUFSZ - .db ZASM_REG_MAXCNT - -; *** Code *** - -symInit: - ld ix, SYM_GLOBAL_REGISTRY - call symClear - ld ix, SYM_LOCAL_REGISTRY - call symClear - ld ix, SYM_CONST_REGISTRY - jp symClear - -; Sets Z according to whether label in (HL) is local (starts with a dot) -symIsLabelLocal: - ld a, '.' - cp (hl) - ret - -symRegisterGlobal: - push ix - ld ix, SYM_GLOBAL_REGISTRY - call symRegister - pop ix - ret - -symRegisterLocal: - push ix - ld ix, SYM_LOCAL_REGISTRY - call symRegister - pop ix - ret - -symRegisterConst: - push ix - ld ix, SYM_CONST_REGISTRY - call symRegister - pop ix - ret - -; Register label in (HL) (minus the ending ":") into the symbol registry in IX -; and set its value in that registry to the value specified in DE. -; If successful, Z is set. Otherwise, Z is unset and A is an error code (ERR_*). -symRegister: - push hl ; --> lvl 1. it's the symbol to add - - call _symIsFull - jr z, .outOfMemory - - ; First, let's get our strlen - call strlen - ld c, a ; save that strlen for later - - call _symFind - jr z, .duplicateError - - ; Is our new name going to make us go out of bounds? - push hl ; --> lvl 2 - push de ; --> lvl 3 - ld d, 0 - ld e, c - add hl, de ; if carry set here, sbc will carry too - ld e, (ix+2) ; DE --> pointer to record list, which is also - ld d, (ix+3) ; the end of names pool - ; DE --> names end - - sbc hl, de ; compares hl and de destructively - pop de ; <-- lvl 3 - pop hl ; <-- lvl 2 - jr nc, .outOfMemory ; HL >= DE - - ; Success. At this point, we have: - ; HL -> where we want to add the string - ; IY -> target record where the value goes - ; DE -> value to register - ; SP -> string to register - - ; Let's start with the record - ld (iy), c ; strlen - ld (iy+1), e - ld (iy+2), d - - ; Good! now, the string. Destination is in HL, source is in SP - ex de, hl ; dest is in DE - pop hl ; <-- lvl 1. string to register - ; Copy HL into DE until we reach null char - call strcpyM - - ; Last thing: increase record count - ld l, (ix+2) - ld h, (ix+3) - inc (hl) - xor a ; sets Z - ret - -.outOfMemory: - pop hl ; <-- lvl 1 - ld a, ERR_OOM - jp unsetZ - -.duplicateError: - pop hl ; <-- lvl 1 - ld a, ERR_DUPSYM - jp unsetZ ; return - -; Assuming that IX points to a registry, find name HL in its names and make IY -; point to the corresponding record. If it doesn't find anything, IY will -; conveniently point to the next record after the last, and HL to the next -; name insertion point. -; If we find something, Z is set, otherwise unset. -_symFind: - push de - push bc - - call strlen - ld c, a ; save strlen - - ex de, hl ; easier if needle is in DE - - ; IY --> records - ld l, (ix+2) - ld h, (ix+3) - ; first byte is count - ld b, (hl) - inc hl ; first record - push hl \ pop iy - ; HL --> names - ld l, (ix) - ld h, (ix+1) - ; do we have an empty reclist? - xor a - cp b - jr z, .nothing ; zero count? nothing -.loop: - ld a, (iy) ; name len - cp c - jr nz, .skip ; different strlen, can't possibly match. skip - call strncmp - jr z, .end ; match! Z already set, IY and HL placed. -.skip: - ; ok, next! - - push de ; --> lvl 1 - ld de, 0x0003 - add iy, de ; faster and shorter than three inc's - ld e, (iy-3) ; offset is also compulsory, so no extra bytes used - ; (iy-3) holds the name length of the string just processed - add hl, de ; advance HL by (iy-3) characters - pop de ; <-- lvl 1 - - djnz .loop - ; end of the chain, nothing found -.nothing: - call unsetZ -.end: - pop bc - pop de - ret - -; For a given symbol name in (HL), find it in the appropriate symbol register -; and return its value in DE. If (HL) is a local label, the local register is -; searched. Otherwise, the global one. It is assumed that this routine is -; always called when the global registry is selected. Therefore, we always -; reselect it afterwards. -symFindVal: - push ix - call symIsLabelLocal - jr z, .local - ; global. Let's try consts first, then symbols - push hl ; --> lvl 1. we'll need it again if not found. - ld ix, SYM_CONST_REGISTRY - call _symFind - pop hl ; <-- lvl 1 - jr z, .found - ld ix, SYM_GLOBAL_REGISTRY - call _symFind - jr nz, .end -.found: - ; Found! let's fetch value - ld e, (iy+1) - ld d, (iy+2) - jr .end -.local: - ld ix, SYM_LOCAL_REGISTRY - call _symFind - jr z, .found - ; continue to end -.end: - pop ix - ret - -; Clear registry at IX -symClear: - push af - push hl - ld l, (ix+2) - ld h, (ix+3) - ; HL --> reclist count - xor a - ld (hl), a - pop hl - pop af - ret - -; Returns whether register in IX has reached its capacity. -; Sets Z if full, unset if not. -_symIsFull: - push hl - ld l, (ix+2) - ld h, (ix+3) - ld l, (hl) ; record count - ld a, (ix+4) ; max record count - cp l - pop hl - ret - -; Parse string (HL) as far as it can for a valid symbol name (see definition in -; comment at top) for a maximum of SYM_NAME_MAXLEN characters. Puts the parsed -; symbol, null-terminated, in SYM_TMPNAME. Make DE point to SYM_TMPNAME. -; HL is advanced to the character following the last successfully read char. -; Z for success. -; Error conditions: -; 1 - No character parsed. -; 2 - name too long. -symParse: - ld de, SYM_TMPNAME - push bc - ; +1 because we want to loop one extra time to see if the char is good - ; or bad. If it's bad, then fine, proceed as normal. If it's good, then - ; its going to go through djnz and we can return an error then. - ld b, SYM_NAME_MAXLEN+1 -.loop: - ld a, (hl) - ; Set it directly, even if we don't know yet if it's good - ld (de), a - or a ; end of string? - jr z, .end ; easy ending, Z set, HL set - ; Check special symbols first - cp '.' - jr z, .good - cp '_' - jr z, .good - ; lowercase - or 0x20 - cp '0' - jr c, .bad - cp '9'+1 - jr c, .good - cp 'a' - jr c, .bad - cp 'z'+1 - jr nc, .bad -.good: - ; character is valid, continue! - inc hl - inc de - djnz .loop - ; error: string too long - ; NZ is already set from cp 'z'+1 - ; HL is one char too far - dec hl - jr .end -.bad: - ; invalid char, stop where we are. - ; In all cases, we want to null-terminate that string - xor a - ld (de), a - ; HL is good. Now, did we succeed? to know, let's see where B is. - ld a, b - cp SYM_NAME_MAXLEN+1 - ; Our result is the invert of Z - call toggleZ -.end: - ld de, SYM_TMPNAME - pop bc - ret diff --git a/apps/zasm/tok.asm b/apps/zasm/tok.asm deleted file mode 100644 index 5ddd882..0000000 --- a/apps/zasm/tok.asm +++ /dev/null @@ -1,249 +0,0 @@ -; *** Consts *** -.equ TOK_INSTR 0x01 -.equ TOK_DIRECTIVE 0x02 -.equ TOK_LABEL 0x03 -.equ TOK_EOF 0xfe ; end of file -.equ TOK_BAD 0xff - -.equ SCRATCHPAD_SIZE 0x40 -; *** Variables *** -.equ scratchpad TOK_RAMSTART -.equ TOK_RAMEND scratchpad+SCRATCHPAD_SIZE - -; *** Code *** - -; Sets Z is A is ';' or null. -isLineEndOrComment: - cp 0x3b ; ';' - ret z - ; continue to isLineEnd - -; Sets Z is A is CR, LF, or null. -isLineEnd: - or a ; same as cp 0 - ret z - cp CR - ret z - cp LF - ret z - cp '\' - ret - -; Sets Z is A is ' ', ',', ';', CR, LF, or null. -isSepOrLineEnd: - call isWS - ret z - jr isLineEndOrComment - -; Checks whether string at (HL) is a label, that is, whether it ends with a ":" -; Sets Z if yes, unset if no. -; -; If it's a label, we change the trailing ':' char with a null char. It's a bit -; dirty, but it's the easiest way to proceed. -isLabel: - push hl - ld a, ':' - call findchar - ld a, (hl) - cp ':' - jr nz, .nomatch - ; We also have to check that it's our last char. - inc hl - ld a, (hl) - or a ; cp 0 - jr nz, .nomatch ; not a null char following the :. no match. - ; We have a match! - ; Remove trailing ':' - xor a ; Z is set - dec hl - ld (hl), a - jr .end -.nomatch: - call unsetZ -.end: - pop hl - ret - -; Read I/O as long as it's whitespace. When it's not, stop and return the last -; read char in A -_eatWhitespace: - call ioGetB - call isWS - ret nz - jr _eatWhitespace - -; Read ioGetB until a word starts, then read ioGetB as long as there is no -; separator and put that contents in (scratchpad), null terminated, for a -; maximum of SCRATCHPAD_SIZE-1 characters. -; If EOL (\n, \r or comment) or EOF is hit before we could read a word, we stop -; right there. If scratchpad is not big enough, we stop right there and error. -; HL points to scratchpad -; Sets Z if a word could be read, unsets if not. -readWord: - push bc - ; Get to word - call _eatWhitespace - call isLineEndOrComment - jr z, .error - ld hl, scratchpad - ld b, SCRATCHPAD_SIZE-1 - ; A contains the first letter to read - ; Are we opening a double quote? - cp '"' - jr z, .insideQuote - ; Are we opening a single quote? - cp 0x27 ; ' - jr z, .singleQuote -.loop: - ld (hl), a - inc hl - call ioGetB - call isSepOrLineEnd - jr z, .success - cp ',' - jr z, .success - djnz .loop - ; out of space. error. -.error: - ; We need to put the last char we've read back so that gotoNextLine - ; behaves properly. - call ioPutBack - call unsetZ - jr .end -.success: - call ioPutBack - ; null-terminate scratchpad - xor a - ld (hl), a - ld hl, scratchpad -.end: - pop bc - ret -.insideQuote: - ; inside quotes, we accept literal whitespaces, but not line ends. - ld (hl), a - inc hl - call ioGetB - cp '"' - jr z, .loop ; ending the quote ends the word - call isLineEnd - jr z, .error ; ending the line without closing the quote, - ; nope. - djnz .insideQuote - ; out of space. error. - jr .error -.singleQuote: - ; single quote is more straightforward: we have 3 chars and we put them - ; right in scratchpad - ld (hl), a - call ioGetB - or a - jr z, .error - inc hl - ld (hl), a - call ioGetB - cp 0x27 ; ' - jr nz, .error - inc hl - ld (hl), a - jr .loop - -; Reads the next char in I/O. If it's a comma, Set Z and return. If it's not, -; Put the read char back in I/O and unset Z. -readComma: - call _eatWhitespace - cp ',' - ret z - call ioPutBack - jp unsetZ - -; Read ioGetB until we reach the beginning of next line, skipping comments if -; necessary. This skips all whitespace, \n, \r, comments until we reach the -; first non-comment character. Then, we put it back (ioPutBack) and return. -; -; If gotoNextLine encounters anything else than whitespace, comment or line -; separator, we error out (no putback) - -; Sets Z if we reached a new line. Unset if EOF or error. -gotoNextLine: -.loop1: - ; first loop is "strict", that is: we error out on non-whitespace. - call ioGetB - call isSepOrLineEnd - ret nz ; error - or a ; cp 0 - jr z, .eof - call isLineEnd - jr z, .loop3 ; good! - cp 0x3b ; ';' - jr z, .loop2 ; comment starting, go to "fast lane" - jr .loop1 -.loop2: - ; second loop is the "comment loop": anything is valid and we just run - ; until EOL. - call ioGetB - or a ; cp 0 - jr z, .eof - cp '\' ; special case: '\' doesn't count as a line end - ; in a comment. - jr z, .loop2 - call isLineEnd - jr z, .loop3 - jr .loop2 -.loop3: - ; Loop 3 happens after we reach our first line sep. This means that we - ; wade through whitespace until we reach a non-whitespace character. - call ioGetB - or a ; cp 0 - jr z, .eof - cp 0x3b ; ';' - jr z, .loop2 ; oh, another comment! go back to loop2! - call isSepOrLineEnd - jr z, .loop3 - ; Non-whitespace. That's our goal! Put it back - call ioPutBack -.eof: - cp a ; ensure Z - ret - -; Parse line in (HL) and read the next token in BC. The token is written on -; two bytes (B and C). B is a token type (TOK_* constants) and C is an ID -; specific to that token type. -; Advance HL to after the read word. -; If no token matches, TOK_BAD is written to B -tokenize: - call readWord - jr z, .process ; read successful, process into token. - ; Error. It could be EOL, EOF or scraptchpad size problem - ; Whatever it is, calling gotoNextLine is appropriate. If it's EOL - ; that's obviously what we want to do. If it's EOF, we can check - ; it after. If it's a scratchpad overrun, gotoNextLine handles it. - call gotoNextLine - jr nz, .error - or a ; Are we EOF? - jr nz, tokenize ; not EOF? then continue! - ; We're EOF - ld b, TOK_EOF - ret -.process: - call isLabel - jr z, .label - call getInstID - jr z, .instr - call getDirectiveID - jr z, .direc -.error: - ; no match - ld b, TOK_BAD - jr .end -.instr: - ld b, TOK_INSTR - jr .end -.direc: - ld b, TOK_DIRECTIVE - jr .end -.label: - ld b, TOK_LABEL -.end: - ld c, a - ret diff --git a/apps/zasm/util.asm b/apps/zasm/util.asm deleted file mode 100644 index 1c133a7..0000000 --- a/apps/zasm/util.asm +++ /dev/null @@ -1,186 +0,0 @@ -; run RLA the number of times specified in B -rlaX: - ; first, see if B == 0 to see if we need to bail out - inc b - dec b - ret z ; Z flag means we had B = 0 -.loop: rla - djnz .loop - ret - -callHL: - jp (hl) - ret - -; HL - DE -> HL -subDEFromHL: - push af - ld a, l - sub e - ld l, a - ld a, h - sbc a, d - ld h, a - pop af - ret - -; Compares strings pointed to by HL and DE up to A count of characters in a -; case-insensitive manner. -; If equal, Z is set. If not equal, Z is reset. -strncmpI: - push bc - push hl - push de - - ld b, a -.loop: - ld a, (de) - call upcase - ld c, a - ld a, (hl) - call upcase - cp c - jr nz, .end ; not equal? break early. NZ is carried out - ; to the called - or a ; cp 0. If our chars are null, stop the cmp - jr z, .end ; The positive result will be carried to the - ; caller - inc hl - inc de - djnz .loop - ; Success - ; We went through all chars with success. Ensure Z - cp a -.end: - pop de - pop hl - pop bc - ; Because we don't call anything else than CP that modify the Z flag, - ; our Z value will be that of the last cp (reset if we broke the loop - ; early, set otherwise) - ret - -; strcmp, then next. Same thing as strcmp, but case insensitive and if strings -; are not equal, make HL point to the character right after the null -; termination. We assume that the haystack (HL), has uppercase chars. -strcmpIN: - push de ; --> lvl 1 - push hl ; --> lvl 2 - -.loop: - ld a, (de) - call upcase - cp (hl) - jr nz, .notFound ; not equal? break early. - or a ; If our chars are null, stop the cmp - jr z, .found - inc hl - inc de - jr .loop -.found: - pop hl ; <-- lvl 2 - pop de ; <-- lvl 1 - ; Z already set - ret -.notFound: - ; Not found, we skip the string - call strskip - pop de ; <-- lvl 2, junk - pop de ; <-- lvl 1 - ret - -; If string at (HL) starts with ( and ends with ), "enter" into the parens -; (advance HL and put a null char at the end of the string) and set Z. -; Otherwise, do nothing and reset Z. -enterParens: - ld a, (hl) - cp '(' - ret nz ; nothing to do - push hl - ld a, 0 ; look for null char - ; advance until we get null -.loop: - cpi - jp z, .found - jr .loop -.found: - dec hl ; cpi over-advances. go back to null-char - dec hl ; looking at the last char before null - ld a, (hl) - cp ')' - jr nz, .doNotEnter - ; We have parens. While we're here, let's put a null - xor a - ld (hl), a - pop hl ; back at the beginning. Let's advance. - inc hl - cp a ; ensure Z - ret ; we're good! -.doNotEnter: - pop hl - call unsetZ - ret - -; Scans (HL) and sets Z according to whether the string is double quoted, that -; is, starts with a " and ends with a ". If it is double quoted, "enter" them, -; that is, advance HL by one and transform the ending quote into a null char. -; If the string isn't double-enquoted, HL isn't changed. -enterDoubleQuotes: - ld a, (hl) - cp '"' - ret nz - push hl - inc hl - ld a, (hl) - or a ; already end of string? - jr z, .nomatch - xor a - call findchar ; go to end of string - dec hl - ld a, (hl) - cp '"' - jr nz, .nomatch - ; We have a match, replace ending quote with null char - xor a - ld (hl), a - ; Good, let's go back - pop hl - ; ... but one char further - inc hl - cp a ; ensure Z - ret -.nomatch: - call unsetZ - pop hl - ret - -; Find string (HL) in string list (DE) of size B, in a case-insensitive manner. -; Each string is C bytes wide. -; Returns the index of the found string. Sets Z if found, unsets Z if not found. -findStringInList: - push de - push bc -.loop: - ld a, c - call strncmpI - ld a, c - call addDE - jr z, .match - djnz .loop - ; no match, Z is unset - pop bc - pop de - ret -.match: - ; Now, we want the index of our string, which is equal to our initial B - ; minus our current B. To get this, we have to play with our registers - ; and stack a bit. - ld d, b - pop bc - ld a, b - sub d - pop de - cp a ; ensure Z - ret - - diff --git a/avr/README.md b/avr/README.md deleted file mode 100644 index 6f58dc6..0000000 --- a/avr/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# AVR include files - -This folder contains header files that can be included in AVR assembly code. - -These definitions are organized in a manner that is very similar to other -modern AVR assemblers, but most bits definitions (`PINB4`, `WGM01`, etc.) are -absent. This is because there's a lot of them, each symbol takes memory during -assembly and machines doing the assembling might be tight in memory. AVR code -post collapse will have to take the habit of using numerical masks accompanied -by comments describing associated symbols. - -To avoid repeats, those includes are organized in 3 levels. First, there's the -`avr.h` file containing definitions common to all AVR models. Then, there's the -"family" file containing definitions common to a "family" (for example, the -ATtiny 25/45/85). Those definitions are the beefiests. Then, there's the exact -model file, which will typically contain RAM and Flash boundaries. diff --git a/avr/avr.h b/avr/avr.h deleted file mode 100644 index 552a0ee..0000000 --- a/avr/avr.h +++ /dev/null @@ -1,10 +0,0 @@ -; *** CPU registers aliases *** - -.equ SREG_C 0 ; Carry Flag -.equ SREG_Z 1 ; Zero Flag -.equ SREG_N 2 ; Negative Flag -.equ SREG_V 3 ; Two's Complement Overflow Flag -.equ SREG_S 4 ; Sign Bit -.equ SREG_H 5 ; Half Carry Flag -.equ SREG_T 6 ; Bit Copy Storage -.equ SREG_I 7 ; Global Interrupt Enable diff --git a/avr/ops.txt b/avr/ops.txt deleted file mode 100644 index 78d34ce..0000000 --- a/avr/ops.txt +++ /dev/null @@ -1,134 +0,0 @@ -The AVR instruction set is a bit more regular than z80's, which allows us for -simpler upcode spitting logic (simplicity which is lost when we need to take -into account all AVR models and instruction constraints on each models). This -file categorizes all available ops with their opcode signature. X means upcode -bit. - -Categories are in descending order of "popularity" - -Mnemonics with "*" are a bit special. - -### 16-bit - -## Plain - -XXXX XXXX XXXX XXXX - -BREAK, CLC, CLH, CLI, CLN, CLS, CLT, CLV, CLZ, EICALL, EIJMP, ELPM*, ICALL, -IJMP, NOP, RET, RETI, SEC, SEH, SEI, SEN, SES, SET, SEV, SEZ, SLEEP, SPM*, WDR - -## Rd(5) - -XXXX XXXd dddd XXXX - -ASR, COM, DEC, ELPM*, INC, LAC, LAS, LAT, LD*, LPM*, LSL*, LSR, NEG, POP, PUSH, -ROR, ST*, SWAP, XCH - -## Rd(5) + Rr(5) - -XXXX XXrd dddd rrrr - -ADC, ADD, AND, CLR, CP, CPC, CPSE, EOR, MOV, MUL, OR, ROL*, SBC, SUB, -TST* - -## k(7) - -XXXX XXkk kkkk kXXX - -BRCC, BRCS, BREQ, BRGE, BRHC, BRHS, BRID, BRIE, BRLO, BRLT, BRMI, BRNE, BRPL, -BRSH, BRTC, BRTS, BRVC, BRVS - -## Rd(4) + K(8) - -XXXX KKKK dddd KKKK - -ANDI, CBR*, CPI, LDI, ORI, SBCI, SBR, SUBI - -## Rd(5) + bit - -XXXX XXXd dddd Xbbb - -BLD, BST, SBRC, SBRS - -## A(5) + bit - -XXXX XXXX AAAA Abbb - -CBI, SBI, SBIC, SBIS - -## Rd(3) + Rr(3) - -XXXX XXXX Xddd Xrrr - -FMUL, FMULS, FMULSU, MULSU - -## Rd(4) + Rr(4) - -XXXX XXXX dddd rrrr - -MOVW, MULS - -## Rd(5) + A(6) - -XXXX XAAd dddd AAAA - -IN, OUT - -## Rd(4) + k(7) - -XXXX Xkkk dddd kkkk - -LDS*, STS* - -## Rd(2) + K - -XXXX XXXX KKdd KKKK - -ADIW, SBIW - -## Rd(4) - -XXXX XXXX dddd XXXX - -SER - -## K(4) - -XXXX XXXX KKKK XXXX - -DES - -## k(12) - -XXXX kkkk kkkk kkkk - -RCALL, RJMP - -## SREG - -XXXX XXXX Xsss XXXX - -BCLR, BSET - -## SREG + k(7) - -XXXX XXkk kkkk ksss - -BRBC, BRBS - -### 32-bit - -## k(22) - -XXXX XXXk kkkk XXXk -kkkk kkkk kkkk kkkk - -CALL, JMP - -## Rd(5) + k(16) - -XXXX XXXd dddd XXXX -kkkk kkkk kkkk kkkk - -LDS*, STS* - diff --git a/avr/tn25.h b/avr/tn25.h deleted file mode 100644 index 4af6b93..0000000 --- a/avr/tn25.h +++ /dev/null @@ -1,10 +0,0 @@ -.equ FLASHEND 0x03ff ; Note: Word address -.equ IOEND 0x003f -.equ SRAM_START 0x0060 -.equ SRAM_SIZE 128 -.equ RAMEND 0x00df -.equ XRAMEND 0x0000 -.equ E2END 0x007f -.equ EEPROMEND 0x007f -.equ EEADRBITS 7 - diff --git a/avr/tn254585.h b/avr/tn254585.h deleted file mode 100644 index 8f6c462..0000000 --- a/avr/tn254585.h +++ /dev/null @@ -1,74 +0,0 @@ -; *** Registers *** - -.equ SREG 0x3f -.equ SPH 0x3e -.equ SPL 0x3d -.equ GIMSK 0x3b -.equ GIFR 0x3a -.equ TIMSK 0x39 -.equ TIFR 0x38 -.equ SPMCSR 0x37 -.equ MCUCR 0x35 -.equ MCUSR 0x34 -.equ TCCR0B 0x33 -.equ TCNT0 0x32 -.equ OSCCAL 0x31 -.equ TCCR1 0x30 -.equ TCNT1 0x2f -.equ OCR1A 0x2e -.equ OCR1C 0x2d -.equ GTCCR 0x2c -.equ OCR1B 0x2b -.equ TCCR0A 0x2a -.equ OCR0A 0x29 -.equ OCR0B 0x28 -.equ PLLCSR 0x27 -.equ CLKPR 0x26 -.equ DT1A 0x25 -.equ DT1B 0x24 -.equ DTPS 0x23 -.equ DWDR 0x22 -.equ WDTCR 0x21 -.equ PRR 0x20 -.equ EEARH 0x1f -.equ EEARL 0x1e -.equ EEDR 0x1d -.equ EECR 0x1c -.equ PORTB 0x18 -.equ DDRB 0x17 -.equ PINB 0x16 -.equ PCMSK 0x15 -.equ DIDR0 0x14 -.equ GPIOR2 0x13 -.equ GPIOR1 0x12 -.equ GPIOR0 0x11 -.equ USIBR 0x10 -.equ USIDR 0x0f -.equ USISR 0x0e -.equ USICR 0x0d -.equ ACSR 0x08 -.equ ADMUX 0x07 -.equ ADCSRA 0x06 -.equ ADCH 0x05 -.equ ADCL 0x04 -.equ ADCSRB 0x03 - - -; *** Interrupt vectors *** - -.equ INT0addr 0x0001 ; External Interrupt 0 -.equ PCI0addr 0x0002 ; Pin change Interrupt Request 0 -.equ OC1Aaddr 0x0003 ; Timer/Counter1 Compare Match 1A -.equ OVF1addr 0x0004 ; Timer/Counter1 Overflow -.equ OVF0addr 0x0005 ; Timer/Counter0 Overflow -.equ ERDYaddr 0x0006 ; EEPROM Ready -.equ ACIaddr 0x0007 ; Analog comparator -.equ ADCCaddr 0x0008 ; ADC Conversion ready -.equ OC1Baddr 0x0009 ; Timer/Counter1 Compare Match B -.equ OC0Aaddr 0x000a ; Timer/Counter0 Compare Match A -.equ OC0Baddr 0x000b ; Timer/Counter0 Compare Match B -.equ WDTaddr 0x000c ; Watchdog Time-out -.equ USI_STARTaddr 0x000d ; USI START -.equ USI_OVFaddr 0x000e ; USI Overflow - -.equ INT_VECTORS_SIZE 15 ; size in words diff --git a/avr/tn45.h b/avr/tn45.h deleted file mode 100644 index 5868719..0000000 --- a/avr/tn45.h +++ /dev/null @@ -1,9 +0,0 @@ -.equ FLASHEND 0x07ff ; Note: Word address -.equ IOEND 0x003f -.equ SRAM_START 0x0060 -.equ SRAM_SIZE 256 -.equ RAMEND 0x015f -.equ XRAMEND 0x0000 -.equ E2END 0x00ff -.equ EEPROMEND 0x00ff -.equ EEADRBITS 8 diff --git a/avr/tn85.h b/avr/tn85.h deleted file mode 100644 index bd052ce..0000000 --- a/avr/tn85.h +++ /dev/null @@ -1,9 +0,0 @@ -.equ FLASHEND 0x0fff ; Note: Word address -.equ IOEND 0x003f -.equ SRAM_START 0x0060 -.equ SRAM_SIZE 512 -.equ RAMEND 0x025f -.equ XRAMEND 0x0000 -.equ E2END 0x01ff -.equ EEPROMEND 0x01ff -.equ EEADRBITS 9 diff --git a/doc/README.md b/doc/README.md deleted file mode 100644 index 5c84564..0000000 --- a/doc/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Collapse OS documentation - -## User guide - -* [The shell](../apps/basic/README.md) -* [Load code in RAM and run it](load-run-code.md) -* [Using block devices](blockdev.md) -* [Using the filesystem](fs.md) -* [Assembling z80 source from the shell](zasm.md) -* [Writing the glue code](glue-code.md) -* [Understanding the code](understanding-code.md) - -## Hardware - -Some consolidated documentation about various hardware supported by Collapse OS. -Most of that information can already be found elsewhere, but the goal is to have -the most vital documentation in one single place. - -* [TI-83+/TI-84+](ti8x.md) -* [TRS-80 model 4p](trs80-4p.md) diff --git a/doc/blockdev.md b/doc/blockdev.md deleted file mode 100644 index cf3d949..0000000 --- a/doc/blockdev.md +++ /dev/null @@ -1,63 +0,0 @@ -# Using block devices - -The `blockdev.asm` part manage what we call "block devices", an abstraction over -something that we can read a byte to, write a byte to, optionally at arbitrary -offsets. - -A Collapse OS system can define up to `0xff` devices. Those definitions are made -in the glue code, so they are static. - -Definition of block devices happen at include time. It would look like: - - [...] - BLOCKDEV_COUNT .equ 1 - #include "blockdev.asm" - ; List of devices - .dw sdcGetB, sdcPutB - [...] - -That tells `blockdev` that we're going to set up one device, that its GetB and -PutB are the ones defined by `sdc.asm`. - -If your block device is read-only or write-only, use dummy routines. `unsetZ` -is a good choice since it will return with the `Z` flag unset, indicating an -error (dummy methods aren't supposed to be called). - -Each defined block device, in addition to its routine definition, holds a -seek pointer. This seek pointer is used in shell commands described below. - -## Routine definitions - -Parts that implement GetB and PutB do so in a loosely-coupled manner, but -they should try to adhere to the convention, that is: - -**GetB**: Get the byte at position specified by `HL`. If it supports 32-bit - addressing, `DE` contains the high-order bytes. Return the result in - `A`. If there's an error (for example, address out of range), unset - `Z`. This routine is not expected to block. We expect the result to be - immediate. - -**PutB**: The opposite of GetB. Write the character in `A` at specified - position. `Z` unset on error. - -## Shell usage - -`apps/basic/blk.asm` supplies 4 shell commands that you can add to your shell. -See "Optional Modules/blk" in [the shell doc](../apps/basic/README.md). - -### Example - -Let's try an example: You glue yourself a Collapse OS with a mmap starting at -`0xe000` as your 4th device (like it is in the shell emulator). Here's what you -could do to copy memory around: - - > m=0xe000 - > while m<0xe004 getc:poke m a:m=m+1 - [enter "abcd"] - > bsel 3 - > i=0 - > while i<4 getb:puth a:i=i+1 - 61626364> bseek 2 - > getb:puth a - 63> getb:puth a - 64> diff --git a/doc/fs.md b/doc/fs.md deleted file mode 100644 index 025c3ed..0000000 --- a/doc/fs.md +++ /dev/null @@ -1,78 +0,0 @@ -# Using the filesystem - -The Collapse OS filesystem (CFS) is a very simple FS that aims at implementation -simplicity first. It is not efficient or featureful, but allows you to get -play around with the concept of files so that you can conveniently run programs -targeting named blocks of data with in storage. - -The filesystem sits on a block device and there can only be one active -filesystem at once. - -Files are represented by adjacent blocks of `0x100` bytes with `0x20` bytes of -metadata on the first block. That metadata tells the location of the next block -which allows for block iteration. - -To create a file, you must allocate blocks to it and these blocks can't be -grown (you have to delete the file and re-allocate it). When allocating new -files, Collapse OS tries to reuse blocks from deleted files if it can. - -Once "mounted" (turned on with `fson`), you can list files, allocate new files -with `fnew`, mark files as deleted with `fdel` and, more importantly, open files -with `fopen`. - -Opened files are accessed a independent block devices. It's the glue code that -decides how many file handles we'll support and to which block device ID each -file handle will be assigned. - -For example, you could have a system with three block devices, one for ACIA and -one for a SD card and one for a file handle. You would mount the filesystem on -block device `1` (the SD card), then open a file on handle `0` with `fopen 0 -filename`. You would then do `bsel 2` to select your third block device which -is mapped to the file you've just opened. - -## Trying it in the emulator - -The shell emulator in `tools/emul/shell` is geared for filesystem usage. If you -look at `shell_.asm`, you'll see that there are 4 block devices: one for -console, one for fake storage (`fsdev`) and two file handles (we call them -`stdout` and `stdin`, but both are read/write in this context). - -The fake device `fsdev` is hooked to the host system through the `cfspack` -utility. Then the emulated shell is started, it checks for the existence of a -`cfsin` directory and, if it exists, it packs its content into a CFS blob and -shoves it into its `fsdev` storage. - -To, to try it out, do this: - - $ mkdir cfsin - $ echo "Hello!" > cfsin/foo - $ echo "Goodbye!" > cfsin/bar - $ ./shell - -The shell, upon startup, automatically calls `fson` targeting block device `1`, -so it's ready to use: - - > fls - foo - bar - > fopen 0 foo - > bsel 2 - > getb - > puth a - 65 - > getb - > puth a - 6C - > getb - > puth a - 6C - > getb - > puth a - 6F - > getb - > puth a - 21 - > fdel bar - > fls - foo - > diff --git a/doc/glue-code.md b/doc/glue-code.md deleted file mode 100644 index 275bd4f..0000000 --- a/doc/glue-code.md +++ /dev/null @@ -1,163 +0,0 @@ -# Writing the glue code - -Collapse OS's kernel code is loosely knit. It supplies parts that you're -expected to glue together in a "glue code" asm file. Here is what a minimal -glue code for a shell on a Classic [RC2014][rc2014] with an ACIA link would -look like: - - - ; The RAM module is selected on A15, so it has the range 0x8000-0xffff - .equ RAMSTART 0x8000 - .equ RAMEND 0xffff - .equ ACIA_CTL 0x80 ; Control and status. RS off. - .equ ACIA_IO 0x81 ; Transmit. RS on. - - jp init - - ; interrupt hook - .fill 0x38-$ - jp aciaInt - - .inc "err.h" - .inc "ascii.h" - .inc "core.asm" - .inc "str.asm" - .inc "parse.asm" - .equ ACIA_RAMSTART RAMSTART - .inc "acia.asm" - - .equ STDIO_RAMSTART ACIA_RAMEND - .equ STDIO_GETC aciaGetC - .equ STDIO_PUTC aciaPutC - .inc "stdio.asm" - - ; *** BASIC *** - - ; RAM space used in different routines for short term processing. - .equ SCRATCHPAD_SIZE 0x20 - .equ SCRATCHPAD STDIO_RAMEND - .inc "lib/util.asm" - .inc "lib/ari.asm" - .inc "lib/parse.asm" - .inc "lib/fmt.asm" - .equ EXPR_PARSE parseLiteralOrVar - .inc "lib/expr.asm" - .inc "basic/util.asm" - .inc "basic/parse.asm" - .inc "basic/tok.asm" - .equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE - .inc "basic/var.asm" - .equ BUF_RAMSTART VAR_RAMEND - .inc "basic/buf.asm" - .equ BAS_RAMSTART BUF_RAMEND - .inc "basic/main.asm" - - init: - di - ; setup stack - ld sp, RAMEND - im 1 - - call aciaInit - call basInit - ei - jp basStart - -Once this is written, you can build it with `zasm`, which takes code from stdin -and spits binary to stdout. Because out code has includes, however, you need -to supply zasm with include folders or files. The invocation would look like - - emul/zasm/zasm kernel/ apps/ < glue.asm > collapseos.bin - -## Building zasm - -Collapse OS has its own assembler written in z80 assembly. We call it -[zasm][zasm]. Even on a "modern" machine, it is that assembler that is used, -but because it is written in z80 assembler, it needs to be emulated (with -[libz80][libz80]). - -So, the first step is to build zasm. Open `emul/README.md` and follow -instructions there. - -## Platform constants - -The upper part of the code contains platform-related constants, information -related to the platform you're targeting. You might want to put it in an -include file if you're writing multiple glue code that targets the same machine. - -In all cases, `RAMSTART` are necessary. `RAMSTART` is the offset at which -writable memory begins. This is where the different parts store their -variables. - -`RAMEND` is the offset where writable memory stop. This is generally -where we put the stack, but as you can see, setting up the stack is the -responsibility of the glue code, so you can set it up however you wish. - -`ACIA_*` are specific to the `acia` part. Details about them are in `acia.asm`. -If you want to manage ACIA, you need your platform to define these ports. - -## Header code - -Then comes the header code (code at `0x0000`), a task that also is in the glue -code's turf. `jr init` means that we run our `init` routine on boot. - -`jp aciaInt` at `0x38` is needed by the `acia` part. Collapse OS doesn't dictate -a particular interrupt scheme, but some parts might. In the case of `acia`, we -require to be set in interrupt mode 1. - -## Includes - -This is the most important part of the glue code and it dictates what will be -included in your OS. Each part is different and has a comment header explaining -how it works, but there are a couple of mechanisms that are common to all. - -### Defines - -Parts can define internal constants, but also often document a "Defines" part. -These are constant that are expected to be set before you include the file. - -See comment in each part for details. - -### RAM management - -Many parts require variables. They need to know where in RAM to store these -variables. Because parts can be mixed and matched arbitrarily, we can't use -fixed memory addresses. - -This is why each part that needs variable define a `_RAMSTART` -constant that must be defined before we include the part. - -Symmetrically, each part define a `_RAMEND` to indicate where its -last variable ends. - -This way, we can easily and efficiently chain up the RAM of every included part. - -### Tables grafting - -A mechanism that is common to some parts is "table grafting". If a part works -on a list of things that need to be defined by the glue code, it will place a -label at the very end of its source file. This way, it becomes easy for the -glue code to "graft" entries to the table. This approach, although simple and -effective, only works for one table per part. But it's often enough. - -For example, to define block devices: - - [...] - .equ BLOCKDEV_COUNT 4 - .inc "blockdev.asm" - ; List of devices - .dw fsdevGetB, fsdevPutB - .dw stdoutGetB, stdoutPutB - .dw stdinGetB, stdinPutB - .dw mmapGetB, mmapPutB - [...] - -### Initialization - -Then, finally, comes the `init` code. This can be pretty much anything really -and this much depends on the part you select. But if you want a shell, you will -usually end it with `basStart`, which never returns. - -[rc2014]: https://rc2014.co.uk/ -[zasm]: ../emul/README.md -[libz80]: https://github.com/ggambetta/libz80 diff --git a/doc/load-run-code.md b/doc/load-run-code.md deleted file mode 100644 index 9766473..0000000 --- a/doc/load-run-code.md +++ /dev/null @@ -1,127 +0,0 @@ -# Load code in RAM and run it - -Collapse OS likely runs from ROM code. If you need to fiddle with your machine -more deeply, you will want to send arbitrary code to it and run it. You can do -so with the shell's `poke` and `usr` commands. - -For example, let's say that you want to run this simple code that you have -sitting on your "modern" machine and want to execute on your running Collapse OS -machine: - - ld a, (0xa100) - inc a - ld (0xa100), a - ret - -(we must always return at the end of code that we call with `usr`). This will -increase a number at memory address `0xa100`. First, compile it: - - zasm < tosend.asm > tosend.bin - -Now, we'll send that code to address `0xa000`: - - > m=0xa000 - > while m<0xa008 getc:poke m a:m=m+1 - (resulting binary is 8 bytes long) - -Now, at this point, it's a bit delicate. To pipe your binary to your serial -connection, you have to close `screen` with CTRL+A then `:quit` to free your -tty device. Then, you can run: - - cat tosend.bin > /dev/ttyUSB0 (or whatever is your device) - -You can then re-open your connection with screen. You'll have a blank screen, -but if the number of characters sent corresponds to what you gave `poke`, then -Collapse OS will be waiting for a new command. Go ahead, verify that the -transfer was successful with: - - > peek 0a000 - > puth a - 3A - > peek 0a007 - > puth a - C9 - -Good! Now, we can try to run it. Before we run it, let's peek at the value at -`0xa100` (being RAM, it's random): - - > peek 0xa100 - > puth a - 61 - -So, we'll expect this to become `62` after we run the code. Let's go: - - > usr 0xa100 - > peek 0xa100 - > puth a - 62 - -Success! - -## The upload tool - -The serial connection is not always 100% reliable and a bad byte can slip in -when you push your code and that's not fun when you try to debug your code (is -this bad behavior caused by my logic or by a bad serial upload?). Moreover, -sending contents manually can be a hassle. - -To this end, there is a `upload` file in `tools/` (run `make` to build it) that -takes care of loading the file and verify the contents. So, instead of doing -`getc` followed by `poke` followed by your `cat` above, you would have done: - - ./upload /dev/ttyUSB0 a000 tosend.bin - -This clears your basic listing and then types in a basic algorithm to receive -and echo and pre-defined number of bytes. The `upload` tool then sends and read -each byte, verifying that they're the same. Very handy. - -## Labels in RAM code - -If your code contains any label, make sure that you add a `.org` directive at -the beginning of your code with the address you're planning on uploading your -code to. Otherwise, those labels are going to point to wrong addresses. - -## Calling ROM code - -The ROM you run Collapse OS on already has quite a bit of code in it, some of -it could be useful to programs you run from RAM. - -If you know exactly where a routine lives in the ROM, you can `call` the address -directly, no problem. However, getting this information is tedious work and is -likely to change whenever you change the kernel code. - -A good approach is to define yourself a jump table that you put in your glue -code. A good place for this is in the `0x03` to `0x37` range, which is empty -anyways (unless you set yourself up with some `rst` jumps) and is needed to -have a proper interrupt hook at `0x38`. For example, your glue code could look -like (important fact: `jp ` uses 3 bytes): - - jp init - ; JUMP TABLE - jp printstr - jp aciaPutC - - .fill 0x38-$ - jp aciaInt - - init: - [...] - -It then becomes easy to build yourself a predictable and stable jump header, -something you could call `jumptable.inc`: - - .equ JUMP_PRINTSTR 0x03 - .equ JUMP_ACIAPUTC 0x06 - -You can then include that file in your "user" code, like this: - - #include "jumptable.inc" - .org 0xa000 - ld hl, label - call JUMP_PRINTSTR - ret - - label: .db "Hello World!", 0 - -If you load that code at `0xa000` and call it, it will print "Hello World!" by -using the `printstr` routine from `core.asm`. diff --git a/doc/ti8x.md b/doc/ti8x.md deleted file mode 100644 index 590a12c..0000000 --- a/doc/ti8x.md +++ /dev/null @@ -1,38 +0,0 @@ -# TI-83+/TI-84+ - -Texas Instruments is well known for its calculators. Among those, two models -are particularly interesting to us because they have a z80 CPU: the TI-83+ and -TI-84+ (the "+" is important). - -They lack accessible I/O ports, but they have plenty of flash and RAM. Collapse -OS runs on it (see `recipes/ti84`). - -I haven't opened one up yet, but apparently, they have limited scavenging value -because its z80 CPU is packaged in a TI-specific chip. Due to its sturdy design, -and its ample RAM and flash, we could imagine it becoming a valuable piece of -equipment if found intact. - -The best pre-collapse ressource about it is -[WikiTI](http://wikiti.brandonw.net/index.php). - -## Getting software on it - -Getting software to run on it is a bit tricky because it needs to be signed -with TI-issued private keys. Those keys have long been found and are included -in `recipes/ti84`. With the help of the -[mktiupgrade](https://github.com/KnightOS/mktiupgrade), an upgrade file can be -prepared and then sent through the USB port with the help of -[tilp](http://lpg.ticalc.org/prj_tilp/). - -That, however, requires a modern computing environment. As of now, there is no -way of installing Collapse OS on a TI-8X+ calculator from another Collapse OS -system. - -Because it is not on the roadmap to implement complex cryptography in Collapse -OS, the plan is to build a series of pre-signed bootloader images. The -bootloader would then receive data through either the Link jack or the USB port -and write that to flash (I haven't verified that yet, but I hope that data -written to flash this way isn't verified cryptographically by the calculator). - -As modern computing fades away, those pre-signed binaries would become opaque, -but at least, would allow bootstrapping from post-modern computers. diff --git a/doc/trs80-4p.md b/doc/trs80-4p.md deleted file mode 100644 index 4757005..0000000 --- a/doc/trs80-4p.md +++ /dev/null @@ -1,243 +0,0 @@ -# TRS-80 Model 4p - -## Ports - - Address Read Write - FC-FF Cassette in Cassette out, resets - F8-FB Rd printer status Wr to printer - F4-F7 - Drive select - F3 FDC data reg FDC data reg - F2 FDC sector reg FDC sector reg - F1 FDC track reg FDC track reg - F0 FDC status reg FDC cmd reg - EC-EF Reset RTC INT Mode output - EB RS232 recv holding reg RS232 xmit holding reg - EA UART status reg UART/modem control - E9 - Baud rate register - E8 Modem status Master reset/enable - UART control reg - E4-E7 Rd NMI status Wr NMI mask reg - E0-E3 Rd INT status Wr INT mask reg - CF HD status HD cmd - CE HD size/drv/hd HD size/drv/hd - CD HD cylinder high HD cylinder high - CC HD cylinder low HD cylinder low - CB HD sector # HD sector # - CA HD sector cnt HD sector cnt - C9 HD error reg HD write precomp - C8 HD data reg HD data reg - C7 HD CTC chan 3 HD CTC chan 3 - C6 HD CTC chan 2 HD CTC chan 2 - C5 HD CTC chan 1 HD CTC chan 1 - C4 HD CTC chan 0 HD CTC chan 0 - C2-C3 HD device ID - - C1 HD control reg HD Control reg - C0 HD wr prot reg - - 94-9F - - - 90-93 - Sound option - 8C-8F Graphic sel 2 Graphic sel 2 - 8B CRTC Data reg CRTC Data reg - 8A CRTC Control reg CRTC Control reg - 89 CRTC Data reg CRTC Data reg - 88 CRTC Control reg CRTC Control reg - 84-87 - Options reg - 83 - Graphic X reg - 82 - Graphic Y reg - 81 Graphics RAM Graphics RAM - 80 - Graphics options reg - - Bit map - - Address D7 D6 D5 D4 D3 D2 D1 D0 - F8-FB-Rd Busy Paper Select Fault - - - - - EC-EF-Rd (any read causes reset of RTC interrupt) - EC-EF-Wr - CPU - Enable Enable Mode Cass - - Fast EX I/O Altset Select Mot on - E0-E3-Rd - Recv Recv Xmit 10 Bus RTC C Fall C Rise - Error Data Empty int Int Int Int - E0-E3-Wr - Enable Enable En.Xmit Enable Enable Enable Enable - Rec err Rec dat Emp 10 int RTC int CF int CR int - 90-93-Wr - - - - - - - Sound - Bit - 84-87-Wr Page Fix upr Memory Memory Invert 80/64 Select Select - mem bit 1 bit 0 video Bit 1 Bit 0 - -## System memory map - -### Memory map 1 - model III mode - - 0000-1fff ROM A (8K) - 2000-2fff ROM B (4K) - 3000-37ff ROM C (2K) - less 37e8/37e9 - 37e8-37e9 Printer Status Port - 3800-3bff Keyboard - 3c00-3fff Video RAM (page bit selects 1K or 2K) - 4000-7fff RAM (16K system) - 4000-ffff RAM (64K system) - -### Memory map 2 - - 0000-37ff RAM (14K) - 3800-3bff Keyboard - 3c00-3fff Video RAM - 4000-7fff RAM (16K) end of one 32K bank - 8000-ffff RAM (32K) second 32K bank - -### Memory map 3 - - 0000-7fff RAM (32K) bank 1 - 8000-f3ff RAM (29K) bank 2 - f400-f7ff Keyboard - f800-ffff Video RAM - -### Memory map 4 - - 0000-7fff RAM (32K) bank 1 - 8000-ffff RAM (32K) bank 2 - -## TRSDOS memory map - - 0000-25ff Reserved for TRSDOS operations - 2600-2fff Overlay area - 3000-HIGH Free to use - HIGH-ffff Drivers, filters, etc - - Use `MEMORY` command to know value of `HIGH` - -## Supervisor calls - -SVC are made by loading the correct SVC number in A, other params in other regs, -and then call `rst 0x28`. - -Z is pretty much always used for success or as a boolean indicator. It is -sometimes not specified when there's not enough tabular space, but it's there. -When `-` is specified, it means that the routine either never returns or is -always successful. - - Num Name Args Res Desc - 00 IPL - - Reboot the system - 01 KEY - AZ Scan *KI, wait for char - 02 DSP C=char AZ Display character - 03 GET DE=F/DCB AZ Get one byte from device or file - 04 PUT DE=F/DCB C=char AZ Write one byte to device or file - 05 CTL DE=DBC C=func CAZ Output a control byte - 06 PRT C=char AZ Send character to printer - 07 WHERE - HL Locate origin of SVC - 08 KBD - AZ Scan keyboard and return - 09 KEYIN HL=buf b=len c=0 HLBZ Accept a line of input - 0a DSPLY HL=str AZ Display message line - 0b LOGER HL=str AZ Issue log message - 0c LOGOT HL=str AZ Display and log message - 0d MSG DE=F/DCB HL=str AZ Send message to device - 0e PRINT HL=str AZ Print message line - 0f VDCTL special spc Video functions - 10 PAUSE BC=delay - Suspend program execution - 11 PARAM DE=ptbl HL=str Z Parse parameter string - 12 DATE HL=recvbuf HLDE Get date - 13 TIME HL=recvbuf HLDE Get time - 14 CHNIO IX=DCB B=dir C=char - Pass control to next module in device chain - 15 ABORT - - Abort Program - 16 EXIT HL=retcode - Exit to TRSDOS - 18 CMNDI HL=cmd - Exec Cmd w/ return to system - 19 CMNDR HL=cmd HL Exec Cmd - 1a ERROR C=errno - Entry to post an error message - 1b DEBUG - - Enter DEBUG - 1c CKTSK C=slot Z Check if task slot in use - 1d ADTSK C=slot - Remove interrupt level task - 1e RMTSK DE=TCB C=slot - Add an interrupt level task - 1f RPTSK - - Replace task vector - 20 KLTSK - - Remove currently executing task - 21 CKDRV C=drvno Z Check drive - 22 DODIR C=drvno b=func ZBHL Do directory display/buffer - 23 RAMDIR HL=buf B=dno C=func AZ Get directory record or free space - 28 DCSTAT C=drvno Z Test if drive assigned in DCT - 29 SLCT C=drvno AZ Select a new drive - 2a DCINIT C=drvno AZ Initialize the FDC - 2b DCRES C=drvno AZ Reset the FDC - 2c RSTOR C=drvno AZ Issue a FDC RESTORE command - 2d STEPI C=drvno AZ Issue a FDC STEP IN command - 2e SEEK C=drvno DE=addr - Seek a cylinder - 2f RSLCT C=drvno - Test for drive busy - 30 RDHDR HL=buf DCE=addr AZ Read a sector header - 31 RDSEC HL=buf DCE=addr AZ Read a sector - 32 VRSEC DCE=addr AZ Verify sector - 33 RDTRK HL=buf DCE=addr AZ Read a track - 34 HDFMT C=drvno AZ Hard disk format - 35 WRSEC HL=buf DCE=addr AZ Write a sector - 36 WRSSC HL=buf DCE=addr AZ Write system sector - 37 WRTRK HL=buf DCE=addr AZ Write a track - 38 RENAM DE=FCB HL=str AZ Rename file - 39 REMOV DE=D/FCB AZ Remove file or device - 3a INIT HL=buf DE=FCB B=LRL AZ Open or initialize file - 3b OPEN HL=buf DE=FCB B=LRL AZ Open existing file or device - 3c CLOSE DE=FCB/DCB AZ Close a file or device - 3d BKSP DE=FCB AZ Backspace one logical record - 3e CKEOF DE=FCB AZ Check for EOF - 3f LOC DE=FCB BCAZ Calculate current logical record number - 40 LOF DE=FCB BCAZ Calculate the EOF logical record number - 41 PEOF DE=FCB AZ Position to end of file - 42 POSN DE=FCB BC=LRN AZ Position file - 43 READ DE=FCB HL=ptr AZ Read a record - 44 REW DE=FCB AZ Rewind file to beginning - 45 RREAD DE=FCB AZ Reread sector - 46 RWRIT DE=FCB AZ Rewrite sector - 47 SEEKSC DE=FCB - Seek cylinder and sector of record - 48 SKIP DE=FCB AZ Skip a record - 49 VER DE=FCB HLAZ Write and verify a record - 4a WEOF DE=FCB AZ Write end of file - 4b WRITE DE=FCB HL=ptr AZ Write a record - 4c LOAD DE=FCB HLAZ Load program file - 4d RUN DE=FCB HLAZ Run program file - 4e FSPEC HL=buf DE=F/DCB HLDE Assign file or device specification - 4f FEXT DE=FCB HL=str - Set up default file extension - 50 FNAME DE=buf B=DEC C=drv AZHL Get filename - 51 GTDCT C=drvno IY Get drive code table address - 52 GTDCB DE=devname HLAZ Get device control block address - 53 GTMOD DE=modname HLDE Get memory module address - 55 RDSSC HL=buf DCE=addr AZ Read system sector - 57 DIRRD B=dirent C=drvno HLAZ Directory record read - 58 DIRWR B=dirent C=drvno HLAZ Directory record write - 5a MUL8 C*E A Multiply C by E - 5b MUL16 HL*C HLA Multiply HL by C - 5d DIV8 E/C AE Divides E by C - 5e DIV16 HL/C HLA Divides HL by C - 60 DECHEX HL=str BCHL Convert Decimal ASCII to binary - 61 HEXDEC HL=num DE=buf DE Convert binary to decimal ASCII - 62 HEX8 C=num HL=buf HL Convert 1 byte to hex ASCII - 53 HEX16 DE=num HL=buf HL Convert 2 bytes to hex ASCII - 64 HIGH$ B=H/L HL=get/set HLAZ Get or Set HIGH$/LOW$ - 65 FLAGS - IY Point IY to system flag table - 66 BANK B=func C=bank BZ Memory bank use - 67 BREAK HL=vector HL Set Break vector - 68 SOUND B=func - Sound generation - -## Personal reverse engineering - -This section below contains notes about my personal reverse engineering efforts. -I'm not an expert in this, and also, I might not be aware of existing, better -documentation making this information useless. - -### Bootable disk - -I'm wondering what makes a disk bootable to the TRS-80 and how it boots it. -When I read the raw contents of the first sector of the first cylinder of the -TRS-DOS disk, I see that, except for the 3 first bytes (`00fe14`), the rest of -the contents is exactly the same as what is at memory offset `0x0203`, which -seems to indicates that the bootloader simply loads that contents to memory, -leaving the first 3 bytes of RAM to either random contents or some predefined -value (I have `f8f800`). - -A non-bootable disk starts with `00fe14`, but we can see the message "Cannot -boot, DA TA DISK!" at offset `0x2a`. - -I'm not sure what `00fe14` can mean. Disassembled, it's -`nop \ rst 0x28 \ ld b, c`. It makes sense that booting would start with a -service call with parameters set by the bootloader (so we don't know what that -service call actually is), but I'm not sure it's what happens. - -I don't see any reference to the `0x2a` offset in the data from the first -sector, but anyways, booting with the non-bootable disk doesn't actually prints -the aformentioned message, so it might be a wild goose chase. - -In any case, making a disk bootable isn't a concern as long as Collapse OS uses -the TRS-DOS drivers. diff --git a/doc/understanding-code.md b/doc/understanding-code.md deleted file mode 100644 index 7edcb13..0000000 --- a/doc/understanding-code.md +++ /dev/null @@ -1,144 +0,0 @@ -# Understanding the code - -One of the design goals of Collapse OS is that its code base should be easily -understandable in its entirety. Let's help with this with a little walthrough. -We use the basic `rc2014` recipe as a basis for the walkthrough. - -This walkthrough assumes that you know z80 assembly. It is recommended that you -read code conventions in `CODE.md` first. - -Code snippets aren't reproduced here. You have to follow along with code -listing. - -## Power on - -You have a RC2014 classic built with an EEPROM that has the recipe's binary on -it and you're linked to its serial I/O module. What happens when you power it -on and press the reset button (I've always had to press the reset button for -the RC2014 to power on properly. I don't know why. Must be some tricky sync -issue with the components)? - -A freshly booted Z80 starts executing address zero. That address is in your -glue code. The first thing it does is thus `jp init`. Initialization is handled -by `recipes/rc2014/glue.asm`. - -As you can see, it's a fairly straightforward init. Stack at the end of RAM, -interrupt mode 1 (which we use for the ACIA), then individual module -initialization, and finally, BASIC's runloop. - -## ACIA init - -An Asynchronous Communication Interface Adaptor allows serial communication with -another ACIA (ref http://alanclements.org/serialio.html ). The RC2014 uses a -6850 ACIA IC and Collapse OS's `kernel/acia` module was written to interface -with this kind of IC. - -For this module to work, it needs to be wired to the z80 but in a particular -manner (which oh! surprise, the RC2014's Serial I/O module is...): It should use -two ports, R/W. One for access to its status register and one for its access to -its data register. Also, its `INT` line should be wired to the z80 `INT` line -for interrupts to work. - -I won't go into much detail about the wiring: the 6850 seems to have been -designed to be wired thus, so it would kind of be like stating the obvious. - -`aciaInit` in `kernel/acia` is also straightforward. First, it initializes the -input buffer. This buffer is a circular buffer that is filled with high priority -during the interrupt handler at `aciaInt`. It's important that we process input -at high priority to be sure not to miss a byte (there is no buffer overrun -handling in `acia`. Unhandled data is simply lost). - -That buffer will later be emptied by BASIC's main loop. - -Once the input buffer is set up, all that is left is to set up the ACIA itself, -which is configurable through `ACIA_CTL`. Comments in the code are -self-explanatory. Make sure that you use serial config, on the other side, that -is compatible with this config there. - -## BASIC init - -Then comes `basInit` at `apps/basic/main`. This is a bigger app, so there is -more stuff to initialize, but still, it stays straightforward. I'm not going to -explain every line, but give you a recipe for understanding. Every variable as, -above its declaration line, a comment explaining what it does. Refer to it. - -This init method is the first one we see that has sub-methods in it. To quickly -find where they live, be aware that the general convention in Collapse OS code -is to prefix every label with its module name. So, for example, `varInit` lives -in `apps/basic/var`. - -You can also see, in the initialization of `BAS_FINDHOOK`, a common idiom: the -use of `unsetZ` (from `kernel/core`) as a noop that returns an error (in this -case, it just means "command not found"). - -## Sending the prompt - -We're now entering `basStart`, which simply prints Collapse OS' prompt and then -enter its runloop. Let's examine what happens when we call `printstr` (from -`kernel/stdio`). - -`printstr` itself is easy. It iterates over `(HL)` and calls `STDIO_PUTC` for -each char. - -But what is `STDIO_PUTC`? It's a glue-defined routine. Let's go back to -`glue.asm`. You see that `.equ STDIO_PUTC aciaPutC` line is? Well, there you -have it. `call STDIO_PUTC`, in our context, is the exact equivalent of -`call aciaPutC`. Let's go see it. - -Whew! it's straightforward! We do two things here: wait until the ACIA is ready -to transmit (if it's not, it means that it's still in the process of -transmitting the previous character we asked it to transmit), then send that -char straight to the data port. - -## BASIC's runloop - -Once the prompt is sent, we're entering BASIC's runloop at `basLoop`. This loops -forever. - -The first thing it does is to wait for a line to be entered using -`stdioReadLine` from `kernel/stdio`. Let's see what this does. - -Oh, this is a little less straightforward. This routine repeatedly calls -`STDIO_GETC` and puts the result in a stdio-specific buffer, after having echoed -back the received character so that the user sees what she types. - -`STDIO_GETC` is blocking. It always returns a char. - -As you can see in the glue unit, `STDIO_GETC` is mapped to `aciaGetC`. This -routine waits until the ACIA buffer has something in it. Once it does, it reads -one character from it and returns it. - -Back to `stdioReadLine`, we check that we don't have special handling to do, -that is, end of line or deletion. If we don't, we echo back the char, advance -buffer pointer, wait for a new one. - -If we receive a CR or LF, the line is complete, so we return to `basLoop` with -a null-terminated input line in `(HL)`. - -I won't cover the processing of the line by BASIC because it's a bit long and -doesn't help holistic understanding very much, You can read the code. - -Once the line is processed, that the associated command is found and called, we -go back the the beginning of the loop for another ride. - -## When do we receive a character? - -In the above section, we simply wait until the buffer has something in it. But -how will that happen? Through `aciaInt` interrupt. - -When the ACIA receives a new character, it pulls the `INT` line low, which, in -interrupt mode 1, calls `0x38`. In our glue code, we jump to `aciaInt`. - -In `aciaInt`, the first thing we do is to check that we're concerned (the `INT` -line can be triggered by other peripherals and we want to ignore those). To do -so, we poll ACIA's status register and see if its receive buffer is full. - -If yes, then we fetch that char from ACIA, put it in the buffer and return from -interrupt. That's how the buffer gets full. - -## Conclusion - -This walkthrough covers only one simple case, but I hope that it gives you keys -to understanding the whole of Collapse OS. You should be able to start from any -other recipe's glue code and walk through it in a way that is similar to what -we've made here. diff --git a/doc/zasm.md b/doc/zasm.md deleted file mode 100644 index 9eed0d2..0000000 --- a/doc/zasm.md +++ /dev/null @@ -1,26 +0,0 @@ -# Assembling z80 source from the shell - -In its current state, Collapse OS has all you need to assemble z80 source -from within the shell. What you need is: - -* A mounted filesystem with `zasm` on it. -* A block device to read from (can be a file from mounted CFS) -* A block device to write to (can also be a file). - -The emulated shell is already set up with all you need. If you want to run that -on a real machine, you'll have to make sure to provide these requirements. - -The emulated shell has a `hello.asm` file in its mounted filesystem that is -ready to compile. It has two file handles 0 and 1, mapped to blk IDs 1 and 2. -We will open our source file in handle 0 and our dest file in handle 1. Then, -with the power of the `fs` module's autoloader, we'll load our newly compiled -file and execute it! - - Collapse OS - > fnew 1 dest ; create destination file - > fopen 0 hello.asm ; open source file in handle 0 - > fopen 1 dest ; open dest binary in handle 1 - > zasm 1 2 ; assemble source file into binary file - > dest ; call newly compiled file - Assembled from the shell - > ; Awesome! diff --git a/emul/cfsin/count.bas b/emul/cfsin/count.bas deleted file mode 100644 index 30896b0..0000000 --- a/emul/cfsin/count.bas +++ /dev/null @@ -1,5 +0,0 @@ -10 print "Count to 10" -20 a=0 -30 a=a+1 -40 print a -50 if a<10 goto 30 diff --git a/emul/cfsin/hello.asm b/emul/cfsin/hello.asm deleted file mode 100644 index b225fb7..0000000 --- a/emul/cfsin/hello.asm +++ /dev/null @@ -1,10 +0,0 @@ -.inc "user.h" -.org USER_CODE - ld hl, sAwesome - call printstr - xor a ; success - ret - -sAwesome: - .db "Assembled from the shell", 0x0d, 0x0a, 0 - diff --git a/emul/cfsin/readme.txt b/emul/cfsin/readme.txt deleted file mode 100644 index 2fa0272..0000000 --- a/emul/cfsin/readme.txt +++ /dev/null @@ -1,3 +0,0 @@ -The contents of this folder ends up in the emulated shell's fake block device, -mounted as a CFS. The goal of the emulated shell being to tests apps, we compile -all apps into this folder for use in the emulated shell. diff --git a/emul/shell/glue.asm b/emul/shell/glue.asm deleted file mode 100644 index 1889b75..0000000 --- a/emul/shell/glue.asm +++ /dev/null @@ -1,178 +0,0 @@ -.inc "blkdev.h" -.inc "fs.h" -.inc "err.h" -.inc "ascii.h" -.equ RAMSTART 0x2000 -.equ USER_CODE 0x4200 -.equ STDIO_PORT 0x00 -.equ FS_DATA_PORT 0x01 -.equ FS_ADDR_PORT 0x02 - - jp init - -; *** JUMP TABLE *** - jp strncmp - jp upcase - jp findchar - jp blkSelPtr - jp blkSel - jp blkSet - jp blkSeek - jp blkTell - jp blkGetB - jp blkPutB - jp fsFindFN - jp fsOpen - jp fsGetB - jp fsPutB - jp fsSetSize - jp fsOn - jp fsIter - jp fsAlloc - jp fsDel - jp fsHandle - jp printstr - jp printnstr - jp _blkGetB - jp _blkPutB - jp _blkSeek - jp _blkTell - jp printcrlf - jp stdioGetC - jp stdioPutC - jp stdioReadLine - -.inc "core.asm" -.inc "str.asm" - -.equ BLOCKDEV_RAMSTART RAMSTART -.equ BLOCKDEV_COUNT 4 -.inc "blockdev.asm" -; List of devices -.dw fsdevGetB, fsdevPutB -.dw stdoutGetB, stdoutPutB -.dw stdinGetB, stdinPutB -.dw mmapGetB, mmapPutB - - -.equ MMAP_START 0xe000 -.inc "mmap.asm" - -.equ STDIO_RAMSTART BLOCKDEV_RAMEND -.equ STDIO_GETC emulGetC -.equ STDIO_PUTC emulPutC -.inc "stdio.asm" - -.equ FS_RAMSTART STDIO_RAMEND -.equ FS_HANDLE_COUNT 2 -.inc "fs.asm" - -; *** BASIC *** - -; RAM space used in different routines for short term processing. -.equ SCRATCHPAD_SIZE STDIO_BUFSIZE -.equ SCRATCHPAD FS_RAMEND -.inc "lib/util.asm" -.inc "lib/ari.asm" -.inc "lib/parse.asm" -.inc "lib/fmt.asm" -.equ EXPR_PARSE parseLiteralOrVar -.inc "lib/expr.asm" -.inc "basic/util.asm" -.inc "basic/parse.asm" -.inc "basic/tok.asm" -.equ VAR_RAMSTART SCRATCHPAD+SCRATCHPAD_SIZE -.inc "basic/var.asm" -.equ BUF_RAMSTART VAR_RAMEND -.inc "basic/buf.asm" -.equ BFS_RAMSTART BUF_RAMEND -.inc "basic/fs.asm" -.inc "basic/blk.asm" -.equ BAS_RAMSTART BFS_RAMEND -.inc "basic/main.asm" - -init: - di - ; setup stack - ld sp, 0xffff - call fsInit - ld a, 0 ; select fsdev - ld de, BLOCKDEV_SEL - call blkSel - call fsOn - call basInit - ld hl, basFindCmdExtra - ld (BAS_FINDHOOK), hl - jp basStart - -basFindCmdExtra: - ld hl, basFSCmds - call basFindCmd - ret z - ld hl, basBLKCmds - call basFindCmd - ret z - jp basPgmHook - -emulGetC: - ; Blocks until a char is returned - in a, (STDIO_PORT) - cp a ; ensure Z - ret - -emulPutC: - out (STDIO_PORT), a - ret - -fsdevGetB: - ld a, e - out (FS_ADDR_PORT), a - ld a, h - out (FS_ADDR_PORT), a - ld a, l - out (FS_ADDR_PORT), a - in a, (FS_ADDR_PORT) - or a - ret nz - in a, (FS_DATA_PORT) - cp a ; ensure Z - ret - -fsdevPutB: - push af - ld a, e - out (FS_ADDR_PORT), a - ld a, h - out (FS_ADDR_PORT), a - ld a, l - out (FS_ADDR_PORT), a - in a, (FS_ADDR_PORT) - cp 2 ; only A > 1 means error - jr nc, .error ; A >= 2 - pop af - out (FS_DATA_PORT), a - cp a ; ensure Z - ret -.error: - pop af - jp unsetZ ; returns - -.equ STDOUT_HANDLE FS_HANDLES - -stdoutGetB: - ld ix, STDOUT_HANDLE - jp fsGetB - -stdoutPutB: - ld ix, STDOUT_HANDLE - jp fsPutB - -.equ STDIN_HANDLE FS_HANDLES+FS_HANDLE_SIZE - -stdinGetB: - ld ix, STDIN_HANDLE - jp fsGetB - -stdinPutB: - ld ix, STDIN_HANDLE - jp fsPutB diff --git a/emul/shell/shell.c b/emul/shell/shell.c deleted file mode 100644 index 7c8206c..0000000 --- a/emul/shell/shell.c +++ /dev/null @@ -1,208 +0,0 @@ -#include -#include -#include -#include -#include "../emul.h" -#include "shell-bin.h" -#include "../../tools/cfspack/cfs.h" - -/* Collapse OS shell with filesystem - * - * On startup, if "cfsin" directory exists, it packs it as a afke block device - * and loads it in. Upon halting, unpcks the contents of that block device in - * "cfsout" directory. - * - * Memory layout: - * - * 0x0000 - 0x3fff: ROM code from shell.asm - * 0x4000 - 0x4fff: Kernel memory - * 0x5000 - 0xffff: Userspace - * - * I/O Ports: - * - * 0 - stdin / stdout - * 1 - Filesystem blockdev data read/write. Reads and write data to the address - * previously selected through port 2 - */ - -//#define DEBUG -#define MAX_FSDEV_SIZE 0x20000 - -// in sync with glue.asm -#define RAMSTART 0x2000 -#define STDIO_PORT 0x00 -#define FS_DATA_PORT 0x01 -// Controls what address (24bit) the data port returns. To select an address, -// this port has to be written to 3 times, starting with the MSB. -// Reading this port returns an out-of-bounds indicator. Meaning: -// 0 means addr is within bounds -// 1 means that we're equal to fsdev size (error for reading, ok for writing) -// 2 means more than fsdev size (always invalid) -// 3 means incomplete addr setting -#define FS_ADDR_PORT 0x02 - -static uint8_t fsdev[MAX_FSDEV_SIZE] = {0}; -static uint32_t fsdev_ptr = 0; -// 0 = idle, 1 = received MSB (of 24bit addr), 2 = received middle addr -static int fsdev_addr_lvl = 0; -static int running; - -static uint8_t iord_stdio() -{ - int c = getchar(); - if (c == EOF) { - running = 0; - } - return (uint8_t)c; -} - -static uint8_t iord_fsdata() -{ - if (fsdev_addr_lvl != 0) { - fprintf(stderr, "Reading FSDEV in the middle of an addr op (%d)\n", fsdev_ptr); - return 0; - } - if (fsdev_ptr < MAX_FSDEV_SIZE) { -#ifdef DEBUG - fprintf(stderr, "Reading FSDEV at offset %d\n", fsdev_ptr); -#endif - return fsdev[fsdev_ptr]; - } else { - fprintf(stderr, "Out of bounds FSDEV read at %d\n", fsdev_ptr); - return 0; - } -} - -static uint8_t iord_fsaddr() -{ - if (fsdev_addr_lvl != 0) { - return 3; - } else if (fsdev_ptr >= MAX_FSDEV_SIZE) { - fprintf(stderr, "Out of bounds FSDEV addr request at %d / %d\n", fsdev_ptr, MAX_FSDEV_SIZE); - return 2; - } else { - return 0; - } -} - -static void iowr_stdio(uint8_t val) -{ - if (val == 0x04) { // CTRL+D - running = 0; - } else { - putchar(val); - } -} - -static void iowr_fsdata(uint8_t val) -{ - if (fsdev_addr_lvl != 0) { - fprintf(stderr, "Writing to FSDEV in the middle of an addr op (%d)\n", fsdev_ptr); - return; - } - if (fsdev_ptr < MAX_FSDEV_SIZE) { -#ifdef DEBUG - fprintf(stderr, "Writing to FSDEV (%d)\n", fsdev_ptr); -#endif - fsdev[fsdev_ptr] = val; - } else { - fprintf(stderr, "Out of bounds FSDEV write at %d\n", fsdev_ptr); - } -} - -static void iowr_fsaddr(uint8_t val) -{ - if (fsdev_addr_lvl == 0) { - fsdev_ptr = val << 16; - fsdev_addr_lvl = 1; - } else if (fsdev_addr_lvl == 1) { - fsdev_ptr |= val << 8; - fsdev_addr_lvl = 2; - } else { - fsdev_ptr |= val; - fsdev_addr_lvl = 0; - } -} - -int main(int argc, char *argv[]) -{ - FILE *fp = NULL; - while (1) { - int c = getopt(argc, argv, "f:"); - if (c < 0) { - break; - } - switch (c) { - case 'f': - fp = fopen(optarg, "r"); - if (fp == NULL) { - fprintf(stderr, "Can't open %s\n", optarg); - return 1; - } - fprintf(stderr, "Initializing filesystem from %s\n", optarg); - int i = 0; - int c; - while ((c = fgetc(fp)) != EOF && i < MAX_FSDEV_SIZE) { - fsdev[i++] = c & 0xff; - } - if (i == MAX_FSDEV_SIZE) { - fprintf(stderr, "Filesytem image too large.\n"); - return 1; - } - pclose(fp); - break; - default: - fprintf(stderr, "Usage: shell [-f fsdev]\n"); - return 1; - } - } - // Setup fs blockdev - if (fp == NULL) { - fprintf(stderr, "Initializing filesystem from cfsin\n"); - fp = fmemopen(fsdev, MAX_FSDEV_SIZE, "w"); - set_spit_stream(fp); - if (spitdir("cfsin", "", NULL) != 0) { - fprintf(stderr, "Can't initialize filesystem. Leaving blank.\n"); - } - fclose(fp); - } - bool tty = isatty(fileno(stdin)); - struct termios termInfo; - if (tty) { - // Turn echo off: the shell takes care of its own echoing. - if (tcgetattr(0, &termInfo) == -1) { - printf("Can't setup terminal.\n"); - return 1; - } - termInfo.c_lflag &= ~ECHO; - termInfo.c_lflag &= ~ICANON; - tcsetattr(0, TCSAFLUSH, &termInfo); - } - - - Machine *m = emul_init(); - m->ramstart = RAMSTART; - m->iord[STDIO_PORT] = iord_stdio; - m->iord[FS_DATA_PORT] = iord_fsdata; - m->iord[FS_ADDR_PORT] = iord_fsaddr; - m->iowr[STDIO_PORT] = iowr_stdio; - m->iowr[FS_DATA_PORT] = iowr_fsdata; - m->iowr[FS_ADDR_PORT] = iowr_fsaddr; - // initialize memory - for (int i=0; imem[i] = KERNEL[i]; - } - // Run! - running = 1; - - while (running && emul_step()); - - if (tty) { - printf("Done!\n"); - termInfo.c_lflag |= ECHO; - termInfo.c_lflag |= ICANON; - tcsetattr(0, TCSAFLUSH, &termInfo); - emul_printdebug(); - } - return 0; -} diff --git a/emul/shell/user.h b/emul/shell/user.h deleted file mode 100644 index d4a758d..0000000 --- a/emul/shell/user.h +++ /dev/null @@ -1,34 +0,0 @@ -.equ USER_CODE 0x4200 ; in sync with glue.asm - -; *** JUMP TABLE *** -.equ strncmp 0x03 -.equ upcase @+3 -.equ findchar @+3 -.equ blkSelPtr @+3 -.equ blkSel @+3 -.equ blkSet @+3 -.equ blkSeek @+3 -.equ blkTell @+3 -.equ blkGetB @+3 -.equ blkPutB @+3 -.equ fsFindFN @+3 -.equ fsOpen @+3 -.equ fsGetB @+3 -.equ fsPutB @+3 -.equ fsSetSize @+3 -.equ fsOn @+3 -.equ fsIter @+3 -.equ fsAlloc @+3 -.equ fsDel @+3 -.equ fsHandle @+3 -.equ printstr @+3 -.equ printnstr @+3 -.equ _blkGetB @+3 -.equ _blkPutB @+3 -.equ _blkSeek @+3 -.equ _blkTell @+3 -.equ printcrlf @+3 -.equ stdioGetC @+3 -.equ stdioPutC @+3 -.equ stdioReadLine @+3 - diff --git a/emul/zasm/glue.asm b/emul/zasm/glue.asm deleted file mode 100644 index d937398..0000000 --- a/emul/zasm/glue.asm +++ /dev/null @@ -1,131 +0,0 @@ -; Glue code for the emulated environment -.equ RAMSTART 0x4000 -.equ USER_CODE 0x4800 -.equ STDIO_PORT 0x00 -.equ STDIN_SEEK 0x01 -.equ FS_DATA_PORT 0x02 -.equ FS_SEEK_PORT 0x03 -.equ STDERR_PORT 0x04 -.inc "err.h" -.inc "ascii.h" -.inc "blkdev.h" -.inc "fs.h" - -jp init ; 3 bytes -; *** JUMP TABLE *** -jp strncmp -jp upcase -jp findchar -jp blkSel -jp blkSet -jp fsFindFN -jp fsOpen -jp fsGetB -jp _blkGetB -jp _blkPutB -jp _blkSeek -jp _blkTell -jp printstr -jp printcrlf - -.inc "core.asm" -.inc "str.asm" -.equ BLOCKDEV_RAMSTART RAMSTART -.equ BLOCKDEV_COUNT 3 -.inc "blockdev.asm" -; List of devices -.dw emulGetB, unsetZ -.dw unsetZ, emulPutB -.dw fsdevGetB, fsdevPutB - -.equ STDIO_RAMSTART BLOCKDEV_RAMEND -.equ STDIO_GETC noop -.equ STDIO_PUTC stderrPutC -.inc "stdio.asm" - -.equ FS_RAMSTART STDIO_RAMEND -.equ FS_HANDLE_COUNT 0 -.inc "fs.asm" - -init: - di - ld hl, 0xffff - ld sp, hl - ld a, 2 ; select fsdev - ld de, BLOCKDEV_SEL - call blkSel - call fsOn - ; There's a special understanding between zasm.c and this unit: The - ; addresses 0xff00 and 0xff01 contain the two ascii chars to send to - ; zasm as the 3rd argument. - ld a, (0xff00) - ld (.zasmArgs+4), a - ld a, (0xff01) - ld (.zasmArgs+5), a - ld hl, .zasmArgs - call USER_CODE - ; signal the emulator we're done - halt - -.zasmArgs: - .db "0 1 XX", 0 - -; *** I/O *** -emulGetB: - ; the STDIN_SEEK port works by poking it twice. First poke is for high - ; byte, second poke is for low one. - ld a, h - out (STDIN_SEEK), a - ld a, l - out (STDIN_SEEK), a - in a, (STDIO_PORT) - or a ; cp 0 - jr z, .eof - cp a ; ensure z - ret -.eof: - jp unsetZ - -emulPutB: - out (STDIO_PORT), a - cp a ; ensure Z - ret - -stderrPutC: - out (STDERR_PORT), a - cp a ; ensure Z - ret - -fsdevGetB: - ld a, e - out (FS_SEEK_PORT), a - ld a, h - out (FS_SEEK_PORT), a - ld a, l - out (FS_SEEK_PORT), a - in a, (FS_SEEK_PORT) - or a - ret nz - in a, (FS_DATA_PORT) - cp a ; ensure Z - ret - -fsdevPutB: - push af - ld a, e - out (FS_SEEK_PORT), a - ld a, h - out (FS_SEEK_PORT), a - ld a, l - out (FS_SEEK_PORT), a - in a, (FS_SEEK_PORT) - or a - jr nz, .error - pop af - out (FS_DATA_PORT), a - cp a ; ensure Z - ret -.error: - pop af - jp unsetZ ; returns - diff --git a/emul/zasm/kernel.bin b/emul/zasm/kernel.bin deleted file mode 100644 index 79bdf49..0000000 Binary files a/emul/zasm/kernel.bin and /dev/null differ diff --git a/emul/zasm/user.h b/emul/zasm/user.h deleted file mode 100644 index 493f010..0000000 --- a/emul/zasm/user.h +++ /dev/null @@ -1,18 +0,0 @@ -.org 0x4800 ; in sync with USER_CODE in glue.asm -.equ USER_RAMSTART 0x6000 - -; *** JUMP TABLE *** -.equ strncmp 0x03 -.equ upcase @+3 -.equ findchar @+3 -.equ blkSel @+3 -.equ blkSet @+3 -.equ fsFindFN @+3 -.equ fsOpen @+3 -.equ fsGetB @+3 -.equ _blkGetB @+3 -.equ _blkPutB @+3 -.equ _blkSeek @+3 -.equ _blkTell @+3 -.equ printstr @+3 -.equ printcrlf @+3 diff --git a/emul/zasm/zasm.bin b/emul/zasm/zasm.bin deleted file mode 100644 index 0bca4fc..0000000 Binary files a/emul/zasm/zasm.bin and /dev/null differ diff --git a/emul/zasm/zasm.c b/emul/zasm/zasm.c deleted file mode 100644 index 5fc7e35..0000000 --- a/emul/zasm/zasm.c +++ /dev/null @@ -1,269 +0,0 @@ -#include -#include -#include -#include -#include -#include "../emul.h" -#include "../../tools/cfspack/cfs.h" -#include "kernel-bin.h" -#ifdef AVRA -#include "avra-bin.h" -#else -#include "zasm-bin.h" -#endif - -/* zasm reads from a specified blkdev, assemble the file and writes the result - * in another specified blkdev. In our emulator layer, we use stdin and stdout - * as those specified blkdevs. - * - * This executable takes two arguments. Both are optional, but you need to - * specify the first one if you want to get to the second one. - * The first one is the value to send to z80-zasm's 3rd argument (the initial - * .org). Defaults to '00'. - * The second one is the path to a .cfs file to use for includes. - * - * Because the input blkdev needs support for Seek, we buffer it in the emulator - * layer. - * - * Memory layout: - * - * 0x0000 - 0x3fff: ROM code from zasm_glue.asm - * 0x4000 - 0x47ff: RAM for kernel and stack - * 0x4800 - 0x57ff: Userspace code - * 0x5800 - 0xffff: Userspace RAM - * - * I/O Ports: - * - * 0 - stdin / stdout - * 1 - When written to, rewind stdin buffer to the beginning. - */ - -// in sync with zasm_glue.asm -#define USER_CODE 0x4800 -#define STDIO_PORT 0x00 -#define STDIN_SEEK_PORT 0x01 -#define FS_DATA_PORT 0x02 -#define FS_SEEK_PORT 0x03 -#define STDERR_PORT 0x04 - -// Other consts -#define STDIN_BUFSIZE 0x8000 -// When defined, we dump memory instead of dumping expected stdout -//#define MEMDUMP -//#define DEBUG -// By default, we don't spit what zasm prints. Too noisy. Define VERBOSE if -// you want to spit this content to stderr. -//#define VERBOSE -#define MAX_FSDEV_SIZE 0x80000 - -// STDIN buffer, allows us to seek and tell -static uint8_t inpt[STDIN_BUFSIZE]; -static int inpt_size; -static int inpt_ptr; -static uint8_t middle_of_seek_tell = 0; - -static uint8_t fsdev[MAX_FSDEV_SIZE] = {0}; -static uint32_t fsdev_ptr = 0; -static uint8_t fsdev_seek_tell_cnt = 0; - -static uint8_t iord_stdio() -{ - if (inpt_ptr < inpt_size) { - return inpt[inpt_ptr++]; - } else { - return 0; - } -} - -static uint8_t iord_stdin_seek() -{ - if (middle_of_seek_tell) { - middle_of_seek_tell = 0; - return inpt_ptr & 0xff; - } else { -#ifdef DEBUG - fprintf(stderr, "tell %d\n", inpt_ptr); -#endif - middle_of_seek_tell = 1; - return inpt_ptr >> 8; - } -} - -static uint8_t iord_fsdata() -{ - if (fsdev_ptr < MAX_FSDEV_SIZE) { - return fsdev[fsdev_ptr++]; - } else { - return 0; - } -} - -static uint8_t iord_fsseek() -{ - if (fsdev_seek_tell_cnt != 0) { - return fsdev_seek_tell_cnt; - } else if (fsdev_ptr >= MAX_FSDEV_SIZE) { - return 1; - } else { - return 0; - } -} - -static void iowr_stdio(uint8_t val) -{ -// When mem-dumping, we don't output regular stuff. -#ifndef MEMDUMP - putchar(val); -#endif -} - -static void iowr_stdin_seek(uint8_t val) -{ - if (middle_of_seek_tell) { - inpt_ptr |= val; - middle_of_seek_tell = 0; -#ifdef DEBUG - fprintf(stderr, "seek %d\n", inpt_ptr); -#endif - } else { - inpt_ptr = (val << 8) & 0xff00; - middle_of_seek_tell = 1; - } -} - -static void iowr_fsdata(uint8_t val) -{ - if (fsdev_ptr < MAX_FSDEV_SIZE) { - fsdev[fsdev_ptr++] = val; - } -} - -static void iowr_fsseek(uint8_t val) -{ - if (fsdev_seek_tell_cnt == 0) { - fsdev_ptr = val << 16; - fsdev_seek_tell_cnt = 1; - } else if (fsdev_seek_tell_cnt == 1) { - fsdev_ptr |= val << 8; - fsdev_seek_tell_cnt = 2; - } else { - fsdev_ptr |= val; - fsdev_seek_tell_cnt = 0; -#ifdef DEBUG - fprintf(stderr, "FS seek %d\n", fsdev_ptr); -#endif - } -} - -static void iowr_stderr(uint8_t val) -{ -#ifdef VERBOSE - fputc(val, stderr); -#endif -} - -void usage() -{ - fprintf(stderr, "Usage: zasm [-o org] [include-dir-or-file...] < source > binary\n"); -} - -int main(int argc, char *argv[]) -{ - char *init_org = "00"; - while (1) { - int c = getopt(argc, argv, "o:"); - if (c < 0) { - break; - } - switch (c) { - case 'o': - init_org = optarg; - if (strlen(init_org) != 2) { - fprintf(stderr, "Initial org must be a two-character hex string"); - } - break; - default: - usage(); - return 1; - } - } - if (argc-optind > 0) { - FILE *fp = fmemopen(fsdev, MAX_FSDEV_SIZE, "w"); - set_spit_stream(fp); - char *patterns[4] = {"*.h", "*.asm", "*.bin", 0}; - for (int i=optind; iiord[STDIO_PORT] = iord_stdio; - m->iord[STDIN_SEEK_PORT] = iord_stdin_seek; - m->iord[FS_DATA_PORT] = iord_fsdata; - m->iord[FS_SEEK_PORT] = iord_fsseek; - m->iowr[STDIO_PORT] = iowr_stdio; - m->iowr[STDIN_SEEK_PORT] = iowr_stdin_seek; - m->iowr[FS_DATA_PORT] = iowr_fsdata; - m->iowr[FS_SEEK_PORT] = iowr_fsseek; - m->iowr[STDERR_PORT] = iowr_stderr; - // initialize memory - for (int i=0; imem[i] = KERNEL[i]; - } - for (int i=0; imem[i+USER_CODE] = USERSPACE[i]; - } - // glue.asm knows that it needs to fetch these arguments at this address. - m->mem[0xff00] = init_org[0]; - m->mem[0xff01] = init_org[1]; - // read stdin in buffer - inpt_size = 0; - inpt_ptr = 0; - int c = getchar(); - while (c != EOF) { - inpt[inpt_ptr] = c & 0xff; - inpt_ptr++; - if (inpt_ptr == STDIN_BUFSIZE) { - break; - } - c = getchar(); - } - inpt_size = inpt_ptr; - inpt_ptr = 0; - - emul_loop(); -#ifdef MEMDUMP - for (int i=0; i<0x10000; i++) { - putchar(mem[i]); - } -#endif - fflush(stdout); - int res = m->cpu.R1.br.A; - if (res != 0) { - int lineno = m->cpu.R1.wr.HL; - int inclineno = m->cpu.R1.wr.DE; - if (inclineno) { - fprintf( - stderr, - "Error %d on line %d, include line %d\n", - res, - lineno, - inclineno); - } else { - fprintf(stderr, "Error %d on line %d\n", res, lineno); - } - } - return res; -} - diff --git a/kernel/README.md b/kernel/README.md deleted file mode 100644 index 797ea25..0000000 --- a/kernel/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Kernel - -Bits and pieces of code that you can assemble to build a kernel for your -machine. - -These parts are made to be glued together in a single `glue.asm` file you write -yourself. - -This code is designed to be assembled by Collapse OS' own [zasm][zasm]. - -## Scope - -Units in the `kernel/` folder is about device driver, abstractions over them -as well as the file system. Although a typical kernel boots to a shell, the -code for that shell is not considered part of the kernel code (even if, most of -the time, it's assembled in the same binary). Shells are considered userspace -applications (which live in `apps/`). diff --git a/kernel/acia.asm b/kernel/acia.asm deleted file mode 100644 index fe66274..0000000 --- a/kernel/acia.asm +++ /dev/null @@ -1,136 +0,0 @@ -; acia -; -; Manage I/O from an asynchronous communication interface adapter (ACIA). -; provides "aciaPutC" to put c char on the ACIA as well as an input buffer. -; You have to call "aciaInt" on interrupt for this module to work well. -; -; "aciaInit" also has to be called on boot, but it doesn't call "ei" and "im 1", -; which is the responsibility of the main asm file, but is needed. - -; *** DEFINES *** -; ACIA_CTL: IO port for the ACIA's control registers -; ACIA_IO: IO port for the ACIA's data registers -; ACIA_RAMSTART: Address at which ACIA-related variables should be stored in -; RAM. - -; *** CONSTS *** -; size of the input buffer. If our input goes over this size, we start losing -; data. -.equ ACIA_BUFSIZE 0x20 - -; *** VARIABLES *** -; Our input buffer starts there. This is a circular buffer. -.equ ACIA_BUF ACIA_RAMSTART - -; The "read" index of the circular buffer. It points to where the next char -; should be read. If rd == wr, the buffer is empty. Not touched by the -; interrupt. -.equ ACIA_BUFRDIDX ACIA_BUF+ACIA_BUFSIZE -; The "write" index of the circular buffer. Points to where the next char -; should be written. Should only be touched by the interrupt. if wr == rd-1, -; the interrupt will *not* write in the buffer until some space has been freed. -.equ ACIA_BUFWRIDX ACIA_BUFRDIDX+1 -.equ ACIA_RAMEND ACIA_BUFWRIDX+1 - -aciaInit: - ; initialize variables - xor a - ld (ACIA_BUFRDIDX), a ; starts at 0 - ld (ACIA_BUFWRIDX), a - - ; setup ACIA - ; CR7 (1) - Receive Interrupt enabled - ; CR6:5 (00) - RTS low, transmit interrupt disabled. - ; CR4:2 (101) - 8 bits + 1 stop bit - ; CR1:0 (10) - Counter divide: 64 - ld a, 0b10010110 - out (ACIA_CTL), a - ret - -; Increase the circular buffer index in A, properly considering overflow. -; returns value in A. -aciaIncIndex: - inc a - cp ACIA_BUFSIZE - ret nz ; not equal? nothing to do - ; equal? reset - xor a - ret - -; read char in the ACIA and put it in the read buffer -aciaInt: - push af - push hl - - ; Read our character from ACIA into our BUFIDX - in a, (ACIA_CTL) - bit 0, a ; is our ACIA rcv buffer full? - jr z, .end ; no? a interrupt was triggered for nothing. - - ; Load both read and write indexes so we can compare them. To do so, we - ; perform a "fake" read increase and see if it brings it to the same - ; value as the write index. - ld a, (ACIA_BUFRDIDX) - call aciaIncIndex - ld l, a - ld a, (ACIA_BUFWRIDX) - cp l - jr z, .end ; Equal? buffer is full - - push de ; <| - ; Alrighty, buffer not full|. let's write. - ld de, ACIA_BUF ; | - ; A already contains our wr|ite index, add it to DE - call addDE ; | - ; increase our buf ptr whil|e we still have it in A - call aciaIncIndex ; | - ld (ACIA_BUFWRIDX), a ; - ; | - ; And finally, fetch the va|lue and write it. - in a, (ACIA_IO) ; | - ld (de), a ; | - pop de ; <| - -.end: - pop hl - pop af - ei - reti - - -; *** STDIO *** -; These function below follow the stdio API. - -aciaGetC: - push de -.loop: - ld a, (ACIA_BUFWRIDX) - ld e, a - ld a, (ACIA_BUFRDIDX) - cp e - jr z, .loop ; equal? nothing to read. loop - - ; Alrighty, buffer not empty. let's read. - ld de, ACIA_BUF - ; A already contains our read index, add it to DE - call addDE - ; increase our buf ptr while we still have it in A - call aciaIncIndex - ld (ACIA_BUFRDIDX), a - - ; And finally, fetch the value. - ld a, (de) - pop de - ret - -; spits character in A in port SER_OUT -aciaPutC: - push af -.stwait: - in a, (ACIA_CTL) ; get status byte from SER - bit 1, a ; are we still transmitting? - jr z, .stwait ; if yes, wait until we aren't - pop af - out (ACIA_IO), a ; push current char - ret - diff --git a/kernel/ascii.h b/kernel/ascii.h deleted file mode 100644 index 1febc86..0000000 --- a/kernel/ascii.h +++ /dev/null @@ -1,4 +0,0 @@ -.equ BS 0x08 -.equ CR 0x0d -.equ LF 0x0a -.equ DEL 0x7f diff --git a/kernel/blkdev.h b/kernel/blkdev.h deleted file mode 100644 index dc62062..0000000 --- a/kernel/blkdev.h +++ /dev/null @@ -1,8 +0,0 @@ -.equ BLOCKDEV_SEEK_ABSOLUTE 0 -.equ BLOCKDEV_SEEK_FORWARD 1 -.equ BLOCKDEV_SEEK_BACKWARD 2 -.equ BLOCKDEV_SEEK_BEGINNING 3 -.equ BLOCKDEV_SEEK_END 4 - -.equ BLOCKDEV_SIZE 8 - diff --git a/kernel/blockdev.asm b/kernel/blockdev.asm deleted file mode 100644 index 95a7323..0000000 --- a/kernel/blockdev.asm +++ /dev/null @@ -1,302 +0,0 @@ -; blockdev -; -; A block device is an abstraction over something we can read from, write to. -; -; A device that fits this abstraction puts the proper hook into itself, and then -; the glue code assigns a blockdev ID to that device. It then becomes easy to -; access arbitrary devices in a convenient manner. -; -; This module exposes a seek/tell/getb/putb API that is then re-routed to -; underlying drivers. There will eventually be more than one driver type, but -; for now we sit on only one type of driver: random access driver. -; -; *** Random access drivers *** -; -; Random access drivers are expected to supply two routines: GetB and PutB. -; -; GetB: -; Reads one byte at address specified in DE/HL and returns its value in A. -; Sets Z according to whether read was successful: Set if successful, unset -; if not. -; -; Unsuccessful reads generally mean that requested addr is out of bounds (we -; reached EOF). -; -; PutB: -; Writes byte in A at address specified in DE/HL. Sets Z according to whether -; the operation was successful. -; -; Unsuccessful writes generally mean that we're out of bounds for writing. -; -; All routines are expected to preserve unused registers except IX which is -; explicitly protected during GetB/PutB calls. This makes quick "handle+jump" -; definitions possible. - - -; *** DEFINES *** -; BLOCKDEV_COUNT: The number of devices we manage. - -; *** CONSTS *** -; *** VARIABLES *** -; Pointer to the selected block device. A block device is a 8 bytes block of -; memory with pointers to GetB, PutB, and a 32-bit counter, in that order. -.equ BLOCKDEV_SEL BLOCKDEV_RAMSTART -.equ BLOCKDEV_RAMEND @+BLOCKDEV_SIZE - -; *** CODE *** -; Put the pointer to the "regular" blkdev selection in DE -blkSelPtr: - ld de, BLOCKDEV_SEL - -; Select block index specified in A and place them in routine pointers at (DE). -; For example, for a "regular" blkSel, you will want to set DE to BLOCKDEV_SEL. -; Sets Z on success, reset on error. -; If A >= BLOCKDEV_COUNT, it's an error. -blkSel: - cp BLOCKDEV_COUNT - jp nc, unsetZ ; if selection >= device count, error - push af - push de - push hl - - ld hl, blkDevTbl - or a ; cp 0 - jr z, .end ; index is zero? don't loop - push bc ; <| - ld b, a ; | -.loop: ; | - ld a, 4 ; | - call addHL ; | - djnz .loop ; | - pop bc ; <| -.end: - call blkSet - pop hl - pop de - pop af - cp a ; ensure Z - ret - -; Setup blkdev handle in (DE) using routines at (HL). -blkSet: - push af - push de - push hl - push bc - - ld bc, 4 - ldir - ; Initialize pos - ld b, 4 - xor a - ex de, hl - call fill - - pop bc - pop hl - pop de - pop af - ret - -_blkInc: - ret nz ; don't advance when in error condition - push af - push hl - ld a, BLOCKDEV_SEEK_FORWARD - ld hl, 1 - call _blkSeek - pop hl - pop af - ret - -; Reads one byte from selected device and returns its value in A. -; Sets Z according to whether read was successful: Set if successful, unset -; if not. -blkGetB: - push ix - ld ix, BLOCKDEV_SEL - call _blkGetB - pop ix - ret -_blkGetB: - push hl - push de - call _blkTell - call callIXI - pop de - pop hl - jr _blkInc ; advance and return - -; Writes byte in A in current position in the selected device. Sets Z according -; to whether the operation was successful. -blkPutB: - push ix - ld ix, BLOCKDEV_SEL - call _blkPutB - pop ix - ret -_blkPutB: - push ix - push hl - push de - call _blkTell - inc ix ; make IX point to PutB - inc ix - call callIXI - pop de - pop hl - pop ix - jr _blkInc ; advance and return - -; Reads B chars from blkGetB and copy them in (HL). -; Sets Z if successful, unset Z if there was an error. -blkRead: - push ix - ld ix, BLOCKDEV_SEL - call _blkRead - pop ix - ret -_blkRead: - push hl - push bc -.loop: - call _blkGetB - jr nz, .end ; Z already unset - ld (hl), a - inc hl - djnz .loop - cp a ; ensure Z -.end: - pop bc - pop hl - ret - -; Writes B chars to blkPutB from (HL). -; Sets Z if successful, unset Z if there was an error. -blkWrite: - push ix - ld ix, BLOCKDEV_SEL - call _blkWrite - pop ix - ret -_blkWrite: - push hl - push bc -.loop: - ld a, (hl) - call _blkPutB - jr nz, .end ; Z already unset - inc hl - djnz .loop - cp a ; ensure Z -.end: - pop bc - pop hl - ret - -; Seeks the block device in one of 5 modes, which is the A argument: -; 0 : Move exactly to X, X being the HL/DE argument. -; 1 : Move forward by X bytes, X being the HL argument (no DE) -; 2 : Move backwards by X bytes, X being the HL argument (no DE) -; 3 : Move to the end -; 4 : Move to the beginning - -; Set position of selected device to the value specified in HL (low) and DE -; (high). DE is only used for mode 0. -; -; When seeking to an out-of-bounds position, the resulting position will be -; one position ahead of the last valid position. Therefore, GetB after a seek -; to end would always fail. -; -; If the device is "growable", it's possible that seeking to end when calling -; PutB doesn't necessarily result in a failure. -blkSeek: - push ix - ld ix, BLOCKDEV_SEL - call _blkSeek - pop ix - ret -_blkSeek: - cp BLOCKDEV_SEEK_FORWARD - jr z, .forward - cp BLOCKDEV_SEEK_BACKWARD - jr z, .backward - cp BLOCKDEV_SEEK_BEGINNING - jr z, .beginning - cp BLOCKDEV_SEEK_END - jr z, .end - ; all other modes are considered absolute - ld (ix+4), e - ld (ix+5), d - ld (ix+6), l - ld (ix+7), h - ret -.forward: - push bc ; <-| - push hl ; <|| - ld l, (ix+6) ; || low byte - ld h, (ix+7) ; || - pop bc ; <|| - add hl, bc ; | - pop bc ; <-| - ld (ix+6), l - ld (ix+7), h - ret nc ; no carry? no need to adjust high byte - ; carry, adjust high byte - inc (ix+4) - ret nz - inc (ix+5) - ret -.backward: - and a ; clear carry - push bc ; <-| - push hl ; <|| - ld l, (ix+6) ; || low byte - ld h, (ix+7) ; || - pop bc ; <|| - sbc hl, bc ; | - pop bc ; <-| - ld (ix+6), l - ld (ix+7), h - ret nc ; no carry? no need to adjust high byte - ld a, 0xff - dec (ix+4) - cp (ix+4) - ret nz - ; we decremented from 0 - dec (ix+5) - ret -.beginning: - xor a - ld (ix+4), a - ld (ix+5), a - ld (ix+6), a - ld (ix+7), a - ret -.end: - ld a, 0xff - ld (ix+4), a - ld (ix+5), a - ld (ix+6), a - ld (ix+7), a - ret - -; Returns the current position of the selected device in HL (low) and DE (high). -blkTell: - push ix - ld ix, BLOCKDEV_SEL - call _blkTell - pop ix - ret -_blkTell: - ld e, (ix+4) - ld d, (ix+5) - ld l, (ix+6) - ld h, (ix+7) - ret - -; This label is at the end of the file on purpose: the glue file should include -; a list of device routine table entries just after the include. Each line -; has 2 word addresses: GetB and PutB. An entry could look like: -; .dw mmapGetB, mmapPutB -blkDevTbl: diff --git a/kernel/core.asm b/kernel/core.asm deleted file mode 100644 index 3091bcd..0000000 --- a/kernel/core.asm +++ /dev/null @@ -1,85 +0,0 @@ -; core -; -; Routines used pretty much all everywhere. Unlike all other kernel units, -; this unit is designed to be included directly by userspace apps, not accessed -; through jump tables. The reason for this is that jump tables are a little -; costly in terms of machine cycles and that these routines are not very costly -; in terms of binary space. -; Therefore, this unit has to stay small and tight because it's repeated both -; in the kernel and in userspace. It should also be exclusively for routines -; used in the kernel. - -; add the value of A into DE -addDE: - push af - add a, e - jr nc, .end ; no carry? skip inc - inc d -.end: - ld e, a - pop af -noop: ; piggy backing on the first "ret" we have - ret - -; add the value of A into HL -; affects carry flag according to the 16-bit addition, Z, S and P untouched. -addHL: - push de - ld d, 0 - ld e, a - add hl, de - pop de - ret - - -; copy (HL) into DE, then exchange the two, utilising the optimised HL instructions. -; ld must be done little endian, so least significant byte first. -intoHL: - push de - ld e, (hl) - inc hl - ld d, (hl) - ex de, hl - pop de - ret - -intoDE: - ex de, hl - call intoHL - ex de, hl ; de preserved by intoHL, so no push/pop needed - ret - -intoIX: - push ix - ex (sp), hl ;swap hl with ix, on the stack - call intoHL - ex (sp), hl ;restore hl from stack - pop ix - ret - -; Call the method (IX) is a pointer to. In other words, call intoIX before -; callIX -callIXI: - push ix - call intoIX - call callIX - pop ix - ret - -; jump to the location pointed to by IX. This allows us to call IX instead of -; just jumping it. We use IX because we seldom use this for arguments. -callIX: - jp (ix) - -callIY: - jp (iy) - -; Ensures that Z is unset (more complicated than it sounds...) -; There are often better inline alternatives, either replacing rets with -; appropriate jmps, or if an 8 bit register is known to not be 0, an inc -; then a dec. If a is nonzero, 'or a' is optimal. -unsetZ: - or a ;if a nonzero, Z reset - ret nz - cp 1 ;if a is zero, Z reset - ret diff --git a/kernel/err.h b/kernel/err.h deleted file mode 100644 index c3b80f5..0000000 --- a/kernel/err.h +++ /dev/null @@ -1,14 +0,0 @@ -; Error codes used throughout the kernel - -; The command that was type isn't known to the shell -.equ SHELL_ERR_UNKNOWN_CMD 0x01 - -; Arguments for the command weren't properly formatted -.equ SHELL_ERR_BAD_ARGS 0x02 - -.equ BLOCKDEV_ERR_OUT_OF_BOUNDS 0x03 -.equ BLOCKDEV_ERR_UNSUPPORTED 0x04 - -; IO routines (GetB, PutB) returned an error in a load/save command -.equ SHELL_ERR_IO_ERROR 0x05 - diff --git a/kernel/fnt/3x5.bin b/kernel/fnt/3x5.bin deleted file mode 100644 index 3145520..0000000 Binary files a/kernel/fnt/3x5.bin and /dev/null differ diff --git a/kernel/fnt/5x7.bin b/kernel/fnt/5x7.bin deleted file mode 100644 index 9589cd3..0000000 Binary files a/kernel/fnt/5x7.bin and /dev/null differ diff --git a/kernel/fnt/7x7.bin b/kernel/fnt/7x7.bin deleted file mode 100644 index 75c1d7a..0000000 Binary files a/kernel/fnt/7x7.bin and /dev/null differ diff --git a/kernel/fnt/mgm.asm b/kernel/fnt/mgm.asm deleted file mode 100644 index 70cf4d3..0000000 --- a/kernel/fnt/mgm.asm +++ /dev/null @@ -1,32 +0,0 @@ -; Font management -; -; There can only ever be one active font. -; -; *** Defines *** -; FNT_DATA: Pointer to the beginning of the binary font data to work with. -; FNT_WIDTH: Width of the font. -; FNT_HEIGHT: Height of the font. -; -; *** Code *** - -; If A is in the range 0x20-0x7e, make HL point to the beginning of the -; corresponding glyph and set Z to indicate success. -; If A isn't in the range, do nothing and unset Z. -fntGet: - cp 0x20 - ret c ; A < 0x20. Z was unset by cp - cp 0x7f - jp nc, unsetZ ; A >= 0x7f. Z might be set - - push af ; --> lvl 1 - push bc ; --> lvl 2 - sub 0x20 - ld hl, FNT_DATA - ld b, FNT_HEIGHT -.loop: - call addHL - djnz .loop - pop bc ; <-- lvl 2 - pop af ; <-- lvl 1 - cp a ; set Z - ret diff --git a/kernel/fs.asm b/kernel/fs.asm deleted file mode 100644 index b574d79..0000000 --- a/kernel/fs.asm +++ /dev/null @@ -1,575 +0,0 @@ -; fs -; -; Collapse OS filesystem (CFS) is not made to be convenient, but to be simple. -; This is little more than "named storage blocks". Characteristics: -; -; * a filesystem sits upon a blockdev. It needs GetB, PutB, Seek. -; * No directory. Use filename prefix to group. -; * First block of each file has metadata. Others are raw data. -; * No FAT. Files are a chain of blocks of a predefined size. To enumerate -; files, you go through metadata blocks. -; * Fixed allocation. File size is determined at allocation time and cannot be -; grown, only shrunk. -; * New allocations try to find spots to fit in, but go at the end if no spot is -; large enough. -; * Block size is 0x100, max block count per file is 8bit, that means that max -; file size: 64k - metadata overhead. -; -; *** Selecting a "source" blockdev -; -; This unit exposes "fson" shell command to "mount" CFS upon the currently -; selected device, at the point where its seekptr currently sits. This checks -; if we have a valid first block and spits an error otherwise. -; -; "fson" takes an optional argument which is a number. If non-zero, we don't -; error out if there's no metadata: we create a new CFS fs with an empty block. -; -; The can only be one "mounted" fs at once. Selecting another blockdev through -; "bsel" doesn't affect the currently mounted fs, which can still be interacted -; with (which is important if we want to move data around). -; -; *** Block metadata -; -; At the beginning of the first block of each file, there is this data -; structure: -; -; 3b: Magic number "CFS" -; 1b: Allocated block count, including the first one. Except for the "ending" -; block, this is never zero. -; 2b: Size of file in bytes (actually written). Little endian. -; 26b: file name, null terminated. last byte must be null. -; -; That gives us 32 bytes of metadata for first first block, leaving a maximum -; file size of 0xffe0. -; -; *** Last block of the chain -; -; The last block of the chain is either a block that has no valid block next to -; it or a block that reports a 0 allocated block count. -; -; However, to simplify processing, whenever fsNext encounter a chain end of the -; first type (a valid block with > 0 allocated blocks), it places an empty block -; at the end of the chain. This makes the whole "end of chain" processing much -; easier: we assume that we always have a 0 block at the end. -; -; *** Deleted files -; -; When a file is deleted, its name is set to null. This indicates that the -; allocated space is up for grabs. -; -; *** File "handles" -; -; Programs will not typically open files themselves. How it works with CFS is -; that it exposes an API to plug target files in a blockdev ID. This all -; depends on how you glue parts together, but ideally, you'll have two -; fs-related blockdev IDs: one for reading, one for writing. -; -; Being plugged into the blockdev system, programs will access the files as they -; would with any other block device. -; -; *** Creating a new FS -; -; A valid Collapse OS filesystem is nothing more than the 3 bytes 'C', 'F', 'S' -; next to each other. Placing them at the right place is all you have to do to -; create your FS. - -; *** DEFINES *** -; Number of handles we want to support -; FS_HANDLE_COUNT -; -; *** VARIABLES *** -; A copy of BLOCKDEV_SEL when the FS was mounted. 0 if no FS is mounted. -.equ FS_BLK FS_RAMSTART -; Offset at which our FS start on mounted device -; This pointer is 32 bits. 32 bits pointers are a bit awkward: first two bytes -; are high bytes *low byte first*, and then the low two bytes, same order. -; When loaded in HL/DE, the four bytes are loaded in this order: E, D, L, H -.equ FS_START @+BLOCKDEV_SIZE -; This variable below contain the metadata of the last block we moved -; to. We read this data in memory to avoid constant seek+read operations. -.equ FS_META @+4 -.equ FS_HANDLES @+FS_METASIZE -.equ FS_RAMEND @+FS_HANDLE_COUNT*FS_HANDLE_SIZE - -; *** DATA *** -P_FS_MAGIC: - .db "CFS", 0 - -; *** CODE *** - -fsInit: - xor a - ld hl, FS_BLK - ld b, FS_RAMEND-FS_BLK - jp fill - -; *** Navigation *** - -; Seek to the beginning. Errors out if no FS is mounted. -; Sets Z if success, unset if error -fsBegin: - call fsIsOn - ret nz - push hl - push de - push af - ld de, (FS_START) - ld hl, (FS_START+2) - ld a, BLOCKDEV_SEEK_ABSOLUTE - call fsblkSeek - pop af - pop de - pop hl - call fsReadMeta - jp fsIsValid ; sets Z, returns - -; Change current position to the next block with metadata. If it can't (if this -; is the last valid block), doesn't move. -; Sets Z according to whether we moved. -fsNext: - push bc - push hl - ld a, (FS_META+FS_META_ALLOC_OFFSET) - or a ; cp 0 - jr z, .error ; if our block allocates 0 blocks, this is the - ; end of the line. - ld b, a ; we will seek A times -.loop: - ld a, BLOCKDEV_SEEK_FORWARD - ld hl, FS_BLOCKSIZE - call fsblkSeek - djnz .loop - call fsReadMeta - jr nz, .createChainEnd - call fsIsValid - jr nz, .createChainEnd - ; We're good! We have a valid FS block. - ; Meta is already read. Nothing to do! - cp a ; ensure Z - jr .end -.createChainEnd: - ; We are on an invalid block where a valid block should be. This is - ; the end of the line, but we should mark it a bit more explicitly. - ; Let's initialize an empty block - call fsInitMeta - call fsWriteMeta - ; continue out to error condition: we're still at the end of the line. -.error: - call unsetZ -.end: - pop hl - pop bc - ret - -; Reads metadata at current fsblk and place it in FS_META. -; Returns Z according to whether the operation succeeded. -fsReadMeta: - push bc - push hl - ld b, FS_METASIZE - ld hl, FS_META - call fsblkRead ; Sets Z - pop hl - pop bc - ret nz - ; Only rewind on success - jr _fsRewindAfterMeta - -; Writes metadata in FS_META at current fsblk. -; Returns Z according to whether the fsblkWrite operation succeeded. -fsWriteMeta: - push bc - push hl - ld b, FS_METASIZE - ld hl, FS_META - call fsblkWrite ; Sets Z - pop hl - pop bc - ret nz - ; Only rewind on success - jr _fsRewindAfterMeta - -_fsRewindAfterMeta: - ; return back to before the read op - push af - push hl - ld a, BLOCKDEV_SEEK_BACKWARD - ld hl, FS_METASIZE - call fsblkSeek - pop hl - pop af - ret - -; Initializes FS_META with "CFS" followed by zeroes -fsInitMeta: - push af - push bc - push de - push hl - ld hl, P_FS_MAGIC - ld de, FS_META - ld bc, 3 - ldir - xor a - ld hl, FS_META+3 - ld b, FS_METASIZE-3 - call fill - pop hl - pop de - pop bc - pop af - ret - -; Create a new file with A blocks allocated to it and with its new name at -; (HL). -; Before doing so, enumerate all blocks in search of a deleted file with -; allocated space big enough. If it does, it will either take the whole space -; if the allocated space asked is exactly the same, or of it isn't, split the -; free space in 2 and create a new deleted metadata block next to the newly -; created block. -; Places fsblk to the newly allocated block. You have to write the new -; filename yourself. -fsAlloc: - push bc - push de - ld c, a ; Let's store our A arg somewhere... - call fsBegin - jr nz, .end ; not a valid block? hum, something's wrong - ; First step: find last block - push hl ; keep HL for later -.loop1: - call fsNext - jr nz, .found ; end of the line - call fsIsDeleted - jr nz, .loop1 ; not deleted? loop - ; This is a deleted block. Maybe it fits... - ld a, (FS_META+FS_META_ALLOC_OFFSET) - cp c ; Same as asked size? - jr z, .found ; yes? great! - ; TODO: handle case where C < A (block splitting) - jr .loop1 -.found: - ; We've reached last block. Two situations are possible at this point: - ; 1 - the block is the "end of line" block - ; 2 - the block is a deleted block that we we're re-using. - ; In both case, the processing is the same: write new metadata. - ; At this point, the blockdev is placed right where we want to allocate - ; But first, let's prepare the FS_META we're going to write - call fsInitMeta - ld a, c ; C == the number of blocks user asked for - ld (FS_META+FS_META_ALLOC_OFFSET), a - pop hl ; now we want our HL arg - ; TODO: stop after null char. we're filling meta with garbage here. - ld de, FS_META+FS_META_FNAME_OFFSET - ld bc, FS_MAX_NAME_SIZE - ldir - ; Good, FS_META ready. - ; Ok, now we can write our metadata - call fsWriteMeta -.end: - pop de - pop bc - ret - -; Place fsblk to the filename with the name in (HL). -; Sets Z on success, unset when not found. -fsFindFN: - push de - call fsBegin - jr nz, .end ; nothing to find, Z is unset - ld a, FS_MAX_NAME_SIZE -.loop: - ld de, FS_META+FS_META_FNAME_OFFSET - call strncmp - jr z, .end ; Z is set - call fsNext - jr z, .loop - ; End of the chain, not found - ; Z already unset -.end: - pop de - ret - -; *** Metadata *** - -; Sets Z according to whether the current block in FS_META is valid. -; Don't call other FS routines without checking block validity first: other -; routines don't do checks. -fsIsValid: - push hl - push de - ld a, 3 - ld hl, FS_META - ld de, P_FS_MAGIC - call strncmp - ; The result of Z is our result. - pop de - pop hl - ret - -; Returns whether current block is deleted in Z flag. -fsIsDeleted: - ld a, (FS_META+FS_META_FNAME_OFFSET) - or a ; Z flag is our answer - ret - -; *** blkdev methods *** -; When "mounting" a FS, we copy the current blkdev's routine privately so that -; we can still access the FS even if blkdev selection changes. These routines -; below mimic blkdev's methods, but for our private mount. - -fsblkGetB: - push ix - ld ix, FS_BLK - call _blkGetB - pop ix - ret - -fsblkRead: - push ix - ld ix, FS_BLK - call _blkRead - pop ix - ret - -fsblkPutB: - push ix - ld ix, FS_BLK - call _blkPutB - pop ix - ret - -fsblkWrite: - push ix - ld ix, FS_BLK - call _blkWrite - pop ix - ret - -fsblkSeek: - push ix - ld ix, FS_BLK - call _blkSeek - pop ix - ret - -fsblkTell: - push ix - ld ix, FS_BLK - call _blkTell - pop ix - ret - -; *** Handling *** - -; Open file at current position into handle at (IX) -fsOpen: - push hl - push af - ; Starting pos - ld a, (FS_BLK+4) - ld (ix), a - ld a, (FS_BLK+5) - ld (ix+1), a - ld a, (FS_BLK+6) - ld (ix+2), a - ld a, (FS_BLK+7) - ld (ix+3), a - ; file size - ld hl, (FS_META+FS_META_FSIZE_OFFSET) - ld (ix+4), l - ld (ix+5), h - pop af - pop hl - ret - -; Place FS blockdev at proper position for file handle in (IX) at position HL. -fsPlaceH: - push af - push de - push hl - ; Move fsdev to beginning of block - ld e, (ix) - ld d, (ix+1) - ld l, (ix+2) - ld h, (ix+3) - ld a, BLOCKDEV_SEEK_ABSOLUTE - call fsblkSeek - - ; skip metadata - ld a, BLOCKDEV_SEEK_FORWARD - ld hl, FS_METASIZE - call fsblkSeek - - pop hl - pop de - - ; go to specified pos - ld a, BLOCKDEV_SEEK_FORWARD - call fsblkSeek - pop af - ret - -; Sets Z according to whether HL is within bounds for file handle at (IX), that -; is, if it is smaller than file size. -fsWithinBounds: - ld a, h - cp (ix+5) - jr c, .within ; H < (IX+5) - jp nz, unsetZ ; H > (IX+5) - ; H == (IX+5) - ld a, l - cp (ix+4) - jp nc, unsetZ ; L >= (IX+4) -.within: - cp a ; ensure Z - ret - -; Set size of file handle (IX) to value in HL. -; This writes directly in handle's metadata. -fsSetSize: - push hl ; --> lvl 1 - ld hl, 0 - call fsPlaceH ; fs blkdev is now at beginning of content - ; we need the blkdev to be on filesize's offset - ld hl, FS_METASIZE-FS_META_FSIZE_OFFSET - ld a, BLOCKDEV_SEEK_BACKWARD - call fsblkSeek - pop hl ; <-- lvl 1 - ; blkdev is at the right spot, HL is back to its original value, let's - ; write it both in the metadata block and in its file handle's cache. - push hl ; --> lvl 1 - ; now let's write our new filesize both in blkdev and in file handle's - ; cache. - ld a, l - ld (ix+4), a - call fsblkPutB - ld a, h - ld (ix+5), a - call fsblkPutB - pop hl ; <-- lvl 1 - xor a ; ensure Z - ret - -; Read a byte in handle at (IX) at position HL and put it into A. -; Z is set on success, unset if handle is at the end of the file. -fsGetB: - call fsWithinBounds - jr z, .proceed - ; We want to unset Z, but also return 0 to ensure that a GetB that - ; doesn't check Z doesn't end up with false data. - xor a - jp unsetZ ; returns -.proceed: - push hl - call fsPlaceH - call fsblkGetB - cp a ; ensure Z - pop hl - ret - -; Write byte A in handle (IX) at position HL. -; Z is set on success, unset if handle is at the end of the file. -; TODO: detect end of block alloc -fsPutB: - push hl - call fsPlaceH - call fsblkPutB - pop hl - ; if HL is out of bounds, increase bounds - call fsWithinBounds - ret z - inc hl ; our filesize is now HL+1 - jp fsSetSize - -; Mount the fs subsystem upon the currently selected blockdev at current offset. -; Verify is block is valid and error out if its not, mounting nothing. -; Upon mounting, copy currently selected device in FS_BLK. -fsOn: - push hl - push de - push bc - ; We have to set blkdev routines early before knowing whether the - ; mounting succeeds because methods like fsReadMeta uses fsblk* methods. - ld hl, BLOCKDEV_SEL - ld de, FS_BLK - ld bc, BLOCKDEV_SIZE - ldir ; copy! - call fsblkTell - ld (FS_START), de - ld (FS_START+2), hl - call fsReadMeta - jr nz, .error - call fsIsValid - jr nz, .error - ; success - xor a - jr .end -.error: - ; couldn't mount. Let's reset our variables. - call fsInit - ld a, FS_ERR_NO_FS - or a ; unset Z -.end: - pop bc - pop de - pop hl - ret - -; Sets Z according to whether we have a filesystem mounted. -fsIsOn: - ; check whether (FS_BLK) is zero - push hl - ld hl, (FS_BLK) - ld a, h - or l - jr nz, .mounted - ; not mounted, unset Z - inc a - jr .end -.mounted: - cp a ; ensure Z -.end: - pop hl - ret - -; Iterate over files in active file system and, for each file, call (IY) with -; the file's metadata currently placed. HL is set to FS_META. -; Sets Z on success, unset on error. -; There are no error condition happening midway. If you get an error, then (IY) -; was never called. -fsIter: - call fsBegin - ret nz -.loop: - call fsIsDeleted - ld hl, FS_META - call nz, callIY - call fsNext - jr z, .loop ; Z set? fsNext was successful - cp a ; ensure Z - ret - -; Delete currently active file -; Sets Z on success, unset on error. -fsDel: - call fsIsValid - ret nz - xor a - ; Set filename to zero to flag it as deleted - ld (FS_META+FS_META_FNAME_OFFSET), a - jp fsWriteMeta - -; Given a handle index in A, set DE to point to the proper handle. -fsHandle: - ld de, FS_HANDLES - or a ; cp 0 - ret z ; DE already point to correct handle - push bc - ld b, a -.loop: - ld a, FS_HANDLE_SIZE - call addDE - djnz .loop - pop bc - ret diff --git a/kernel/fs.h b/kernel/fs.h deleted file mode 100644 index 68df440..0000000 --- a/kernel/fs.h +++ /dev/null @@ -1,13 +0,0 @@ -.equ FS_MAX_NAME_SIZE 0x1a -.equ FS_BLOCKSIZE 0x100 -.equ FS_METASIZE 0x20 - -.equ FS_META_ALLOC_OFFSET 3 -.equ FS_META_FSIZE_OFFSET 4 -.equ FS_META_FNAME_OFFSET 6 -; Size in bytes of a FS handle: -; * 4 bytes for starting offset of the FS block -; * 2 bytes for file size -.equ FS_HANDLE_SIZE 6 -.equ FS_ERR_NO_FS 0x5 -.equ FS_ERR_NOT_FOUND 0x6 diff --git a/kernel/grid.asm b/kernel/grid.asm deleted file mode 100644 index cb0fe84..0000000 --- a/kernel/grid.asm +++ /dev/null @@ -1,275 +0,0 @@ -; grid - abstraction for grid-like video output -; -; Collapse OS doesn't support curses-like interfaces: too complicated. However, -; in cases where output don't have to go through a serial interface before -; being displayed, we have usually have access to a grid-like interface. -; -; Direct access to this kind of interface allow us to build an abstraction layer -; that is very much alike curses but is much simpler underneath. This unit is -; this abstraction. -; -; The principle is simple: we have a cell grid of X columns by Y rows and we -; can access those cells by their (X, Y) address. In addition to this, we have -; the concept of an active cursor, which will be indicated visually if possible. -; -; This module provides PutC and GetC routines, suitable for plugging into stdio. -; PutC, for obvious reasons, GetC, for less obvious reasons: We need to wrap -; GetC because we need to update the cursor before calling actual GetC, but -; also, because we need to know when a bulk update ends. -; -; *** Defines *** -; -; GRID_COLS: Number of columns in the grid -; GRID_ROWS: Number of rows in the grid -; GRID_SETCELL: Pointer to routine that sets cell at row D and column E with -; character in A. If C is nonzero, this cell must be displayed, -; if possible, as the cursor. This routine is never called with -; A < 0x20. -; GRID_GETC: Routine that gridGetC will wrap around. -; -; *** Consts *** -.equ GRID_SIZE GRID_COLS*GRID_ROWS - -; *** Variables *** -; Cursor's column -.equ GRID_CURX GRID_RAMSTART -; Cursor's row -.equ GRID_CURY @+1 -; Whether we scrolled recently. We don't refresh the screen immediately when -; scrolling in case we have many lines being spit at once (refreshing the -; display is then very slow). Instead, we wait until the next gridGetC call -.equ GRID_SCROLLED @+1 -; Grid's in-memory buffer of the contents on screen. Because we always push to -; display right after a change, this is almost always going to be a correct -; representation of on-screen display. -; The buffer is organized as a rows of columns. The cell at row Y and column X -; is at GRID_BUF+(Y*GRID_COLS)+X. -.equ GRID_BUF @+1 -.equ GRID_RAMEND @+GRID_SIZE - -; *** Code *** - -gridInit: - xor a - ld b, GRID_RAMEND-GRID_RAMEND - ld hl, GRID_RAMSTART - jp fill - -; Place HL at row D and column E in the buffer -; Destroys A -_gridPlaceCell: - ld hl, GRID_BUF - ld a, d - or a - jr z, .setcol - push de ; --> lvl 1 - ld de, GRID_COLS -.loop: - add hl, de - dec a - jr nz, .loop - pop de ; <-- lvl 1 -.setcol: - ; We're at the proper row, now let's advance to cell - ld a, e - jp addHL - -; Ensure that A >= 0x20 -_gridAdjustA: - cp 0x20 - ret nc - ld a, 0x20 - ret - -; Push row D in the buffer onto the screen. -gridPushRow: - push af - push bc - push de - push hl - ; Cursor off - ld c, 0 - ld e, c - call _gridPlaceCell - ld b, GRID_COLS -.loop: - ld a, (hl) - call _gridAdjustA - ; A, C, D and E have proper values - call GRID_SETCELL - inc hl - inc e - djnz .loop - - pop hl - pop de - pop bc - pop af - ret - -; Clear row D and push contents to screen -gridClrRow: - push af - push bc - push de - push hl - ld e, 0 - call _gridPlaceCell - ld a, ' ' - ld b, GRID_COLS - call fill - call gridPushRow - pop hl - pop de - pop bc - pop af - ret - -gridPushScr: - push de - ld d, GRID_ROWS-1 -.loop: - call gridPushRow - dec d - jp p, .loop - pop de - ret - -; Set character under cursor to A. C is passed to GRID_SETCELL as-is. -gridSetCur: - push de - push hl - push af ; --> lvl 1 - ld a, (GRID_CURY) - ld d, a - ld a, (GRID_CURX) - ld e, a - call _gridPlaceCell - pop af \ push af ; <--> lvl 1 - ld (hl), a - call _gridAdjustA - call GRID_SETCELL - pop af ; <-- lvl 1 - pop hl - pop de - ret - -; Call gridSetCur with C = 1. -gridSetCurH: - push bc - ld c, 1 - call gridSetCur - pop bc - ret - -; Call gridSetCur with C = 0. -gridSetCurL: - push bc - ld c, 0 - call gridSetCur - pop bc - ret - -; Clear character under cursor -gridClrCur: - push af - ld a, ' ' - call gridSetCurL - pop af - ret - -gridLF: - call gridClrCur - push de - push af - ld a, (GRID_CURY) - ; increase A - inc a - cp GRID_ROWS - jr nz, .noscroll - ; bottom reached, stay on last line and scroll screen - push hl - push de - push bc - ld de, GRID_BUF - ld hl, GRID_BUF+GRID_COLS - ld bc, GRID_SIZE-GRID_COLS - ldir - ld hl, GRID_SCROLLED - inc (hl) ; mark as scrolled - pop bc - pop de - pop hl - dec a -.noscroll: - ; A has been increased properly - ld d, a - call gridClrRow - ld (GRID_CURY), a - xor a - ld (GRID_CURX), a - pop af - pop de - ret - -gridBS: - call gridClrCur - push af - ld a, (GRID_CURX) - or a - jr z, .lineup - dec a - ld (GRID_CURX), a - pop af - ret -.lineup: - ; end of line, we need to go up one line. But before we do, are we - ; already at the top? - ld a, (GRID_CURY) - or a - jr z, .end - dec a - ld (GRID_CURY), a - ld a, GRID_COLS-1 - ld (GRID_CURX), a -.end: - pop af - ret - -gridPutC: - cp LF - jr z, gridLF - cp BS - jr z, gridBS - cp ' ' - ret c ; ignore unhandled control characters - - call gridSetCurL - push af ; --> lvl 1 - ; Move cursor - ld a, (GRID_CURX) - cp GRID_COLS-1 - jr z, .incline - ; We just need to increase X - inc a - ld (GRID_CURX), a - pop af ; <-- lvl 1 - ret -.incline: - ; increase line and start anew - call gridLF - pop af ; <-- lvl 1 - ret - -gridGetC: - ld a, (GRID_SCROLLED) - or a - jr z, .nopush - ; We've scrolled recently, update screen - xor a - ld (GRID_SCROLLED), a - call gridPushScr -.nopush: - ld a, ' ' - call gridSetCurH - jp GRID_GETC diff --git a/kernel/kbd.asm b/kernel/kbd.asm deleted file mode 100644 index 8e28742..0000000 --- a/kernel/kbd.asm +++ /dev/null @@ -1,137 +0,0 @@ -; kbd - implement GetC for PS/2 keyboard -; -; It reads raw key codes from a FetchKC routine and returns, if appropriate, -; a proper ASCII char to type. See recipes rc2014/ps2 and sms/kbd. -; -; *** Defines *** -; Pointer to a routine that fetches the last typed keyword in A. Should return -; 0 when nothing was typed. -; KBD_FETCHKC - -; *** Consts *** -.equ KBD_KC_BREAK 0xf0 -.equ KBD_KC_EXT 0xe0 -.equ KBD_KC_LSHIFT 0x12 -.equ KBD_KC_RSHIFT 0x59 - -; *** Variables *** -; Set to previously received scan code -.equ KBD_PREV_KC KBD_RAMSTART -; Whether Shift key is pressed. When not pressed, holds 0. When pressed, holds -; 0x80. This allows for quick shifting in the glyph table. -.equ KBD_SHIFT_ON @+1 -.equ KBD_RAMEND @+1 - -kbdInit: - xor a - ld (KBD_PREV_KC), a - ld (KBD_SHIFT_ON), a - ret - -kbdGetC: - call KBD_FETCHKC - or a - jr z, .nothing - - ; scan code not zero, maybe we have something. - ; Do we need to skip it? - ex af, af' ; save fetched KC - ld a, (KBD_PREV_KC) - ; Whatever the KC, the new A becomes our prev. The easiest way to do - ; this is to do it now. - ex af, af' ; restore KC - ld (KBD_PREV_KC), a - ex af, af' ; restore prev KC - ; If F0 (break code) or E0 (extended code), we skip this code - cp KBD_KC_BREAK - jr z, .break - cp KBD_KC_EXT - jr z, .nothing - ex af, af' ; restore saved KC - ; A scan code over 0x80 is out of bounds or prev KC tell us we should - ; skip. Ignore. - cp 0x80 - jr nc, .nothing - ; No need to skip, code within bounds, we have something! - call .isShift - jr z, .shiftPressed - ; Let's see if there's a ASCII code associated to it. - push hl ; --> lvl 1 - ld hl, KBD_SHIFT_ON - or (hl) ; if shift is on, A now ranges in 0x80-0xff. - ld hl, kbdScanCodes ; no flag changed - call addHL - ld a, (hl) - pop hl ; <-- lvl 1 - or a - jr z, kbdGetC ; no code. - ; We have something! - cp a ; ensure Z - ret -.shiftPressed: - ld a, 0x80 - ld (KBD_SHIFT_ON), a - jr .nothing ; no actual char to return -.break: - ex af, af' ; restore saved KC - call .isShift - jr nz, .nothing - ; We had a shift break, update status - xor a - ld (KBD_SHIFT_ON), a - ; continue to .nothing -.nothing: - ; We have nothing. Before we go further, we'll wait a bit to give our - ; device the time to "breathe". When we're in a "nothing" loop, the z80 - ; hammers the device really fast and continuously generates interrupts - ; on it and it interferes with its other task of reading the keyboard. - xor a -.wait: - inc a - jr nz, .wait - jr kbdGetC -; Whether KC in A is L or R shift -.isShift: - cp KBD_KC_LSHIFT - ret z - cp KBD_KC_RSHIFT - ret - -; A list of the values associated with the 0x80 possible scan codes of the set -; 2 of the PS/2 keyboard specs. 0 means no value. That value is a character that -; can be read in a GetC routine. No make code in the PS/2 set 2 reaches 0x80. -kbdScanCodes: -; 0x00 1 2 3 4 5 6 7 8 9 a b c d e f -.db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,'`', 0 -; 0x10 9 = TAB -.db 0, 0, 0, 0, 0,'q','1', 0, 0, 0,'z','s','a','w','2', 0 -; 0x20 32 = SPACE -.db 0,'c','x','d','e','4','3', 0, 0, 32,'v','f','t','r','5', 0 -; 0x30 -.db 0,'n','b','h','g','y','6', 0, 0, 0,'m','j','u','7','8', 0 -; 0x40 59 = ; -.db 0,',','k','i','o','0','9', 0, 0,'.','/','l', 59,'p','-', 0 -; 0x50 13 = RETURN 39 = ' -.db 0, 0, 39, 0,'[','=', 0, 0, 0, 0, 13,']', 0,'\', 0, 0 -; 0x60 8 = BKSP -.db 0, 0, 0, 0, 0, 0, 8, 0, 0,'1', 0,'4','7', 0, 0, 0 -; 0x70 27 = ESC -.db '0','.','2','5','6','8', 27, 0, 0, 0,'3', 0, 0,'9', 0, 0 - -; Same values, but shifted, exactly 0x80 bytes after kbdScanCodes -; 0x00 1 2 3 4 5 6 7 8 9 a b c d e f -.db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,'~', 0 -; 0x10 9 = TAB -.db 0, 0, 0, 0, 0,'Q','!', 0, 0, 0,'Z','S','A','W','@', 0 -; 0x20 32 = SPACE -.db 0,'C','X','D','E','$','#', 0, 0, 32,'V','F','T','R','%', 0 -; 0x30 -.db 0,'N','B','H','G','Y','^', 0, 0, 0,'M','J','U','&','*', 0 -; 0x40 59 = ; -.db 0,'<','K','I','O',')','(', 0, 0,'>','?','L',':','P','_', 0 -; 0x50 13 = RETURN -.db 0, 0,'"', 0,'{','+', 0, 0, 0, 0, 13,'}', 0,'|', 0, 0 -; 0x60 8 = BKSP -.db 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0 -; 0x70 27 = ESC -.db 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/kernel/mmap.asm b/kernel/mmap.asm deleted file mode 100644 index 82da26f..0000000 --- a/kernel/mmap.asm +++ /dev/null @@ -1,48 +0,0 @@ -; mmap -; -; Block device that maps to memory. -; -; *** DEFINES *** -; MMAP_START: Memory address where the mmap begins -; Memory address where the mmap stops, exclusively (we aren't allowed to access -; that address). -.equ MMAP_LEN 0xffff-MMAP_START - -; Returns absolute addr of memory pointer in HL if HL is within bounds. -; Sets Z on success, unset when out of bounds. -_mmapAddr: - push de - ld de, MMAP_LEN - or a ; reset carry flag - sbc hl, de - jr nc, .outOfBounds ; HL >= DE - add hl, de ; old HL value - ld de, MMAP_START - add hl, de - cp a ; ensure Z - pop de - ret -.outOfBounds: - pop de - jp unsetZ - -mmapGetB: - push hl - call _mmapAddr - jr nz, .end - ld a, (hl) - ; Z already set -.end: - pop hl - ret - - -mmapPutB: - push hl - call _mmapAddr - jr nz, .end - ld (hl), a - ; Z already set -.end: - pop hl - ret diff --git a/kernel/sdc.asm b/kernel/sdc.asm deleted file mode 100644 index 7edbdbf..0000000 --- a/kernel/sdc.asm +++ /dev/null @@ -1,759 +0,0 @@ -; sdc -; -; Manages the initialization of a SD card and implement a block device to read -; and write from/to it, in SPI mode. -; -; Note that SPI can't really be used directly from the z80, so this part -; assumes that you have a device that handles SPI communication on behalf of -; the z80. This device is assumed to work in a particular way. See the -; "rc2014/sdcard" recipe for details. -; -; That device has 3 ports. One write-only port to make CS high, one to make CS -; low (data sent is irrelevant), and one read/write port to send and receive -; bytes with the card through the SPI protocol. The device acts as a SPI master -; and writing to that port initiates a byte exchange. Data from the slave is -; then placed on a buffer that can be read by reading the same port. -; -; It's through that kind of device that this code below is supposed to work. -; -; *** SDC buffers *** -; -; SD card's lowest common denominator in terms of block size is 512 bytes, so -; that's what we deal with. To avoid wastefully reading entire blocks from the -; card for one byte read ops, we buffer the last read block. If a GetB or PutB -; operation is within that buffer, then no interaction with the SD card is -; necessary. -; -; As soon as a GetB or PutB operation is made that is outside the current -; buffer, we load a new block. -; -; When we PutB, we flag the buffer as "dirty". On the next buffer change (during -; an out-of-buffer request or during an explicit "flush" operation), bytes -; currently in the buffer will be written to the SD card. -; -; We hold 2 buffers in memory, each targeting a different sector and with its -; own dirty flag. We do that to avoid wasteful block writing in the case where -; we read data from a file in the SD card, process it and write the result -; right away, in another file on the same card (zasm), on a different sector. -; -; If we only have one buffer in this scenario, we'll end up loading a new sector -; at each GetB/PutB operation and, more importantly, writing a whole block for -; a few bytes each time. This will wear the card prematurely (and be very slow). -; -; With 2 buffers, we solve the problem. Whenever GetB/PutB is called, we first -; look if one of the buffer holds our sector. If not, we see if one of the -; buffer is clean (not dirty). If yes, we use this one. If both are dirty or -; clean, we use any. This way, as long as writing isn't made to random -; addresses, we ensure that we don't write wastefully because read operations, -; even if random, will always use the one buffer that isn't dirty. - -; *** Defines *** -; SDC_PORT_CSHIGH: Port number to make CS high -; SDC_PORT_CSLOW: Port number to make CS low -; SDC_PORT_SPI: Port number to send/receive SPI data - -; *** Consts *** -.equ SDC_BLKSIZE 512 -.equ SDC_MAXTRIES 8 - -; *** Variables *** -; This is a pointer to the currently selected buffer. This points to the BUFSEC -; part, that is, two bytes before actual content begins. -.equ SDC_BUFPTR SDC_RAMSTART -; Count the number of times we tried a particular read or write operation. When -; CRC check fail, we retry. After SDC_MAXTRIES failures, we stop. -.equ SDC_RETRYCNT SDC_BUFPTR+2 -; Sector number currently in SDC_BUF1. Little endian like any other z80 word. -.equ SDC_BUFSEC1 SDC_RETRYCNT+1 -; Whether the buffer has been written to. 0 means clean. 1 means dirty. -.equ SDC_BUFDIRTY1 SDC_BUFSEC1+2 -; The contents of the buffer. -.equ SDC_BUF1 SDC_BUFDIRTY1+1 -; CRC bytes for the buffer. They're placed after the contents because that makes -; things easier processing-wise. Because the SD card sends them right after the -; contents, all we need to do is read SDC_BLKSIZE+2. -; IMPORTANT NOTE: This is big endian. The SD card sends the MSB first, so we -; keep it in memory this way. -.equ SDC_CRC1 SDC_BUF1+SDC_BLKSIZE - -; second buffer has the same structure as the first. -.equ SDC_BUFSEC2 SDC_CRC1+2 -.equ SDC_BUFDIRTY2 SDC_BUFSEC2+2 -.equ SDC_BUF2 SDC_BUFDIRTY2+1 -.equ SDC_CRC2 SDC_BUF2+SDC_BLKSIZE -.equ SDC_RAMEND SDC_CRC2+2 - -; *** Code *** -; Wake the SD card up. After power up, a SD card has to receive at least 74 -; dummy clocks with CS and DI high. We send 80. -sdcWakeUp: - out (SDC_PORT_CSHIGH), a - ld b, 10 ; 10 * 8 == 80 - ld a, 0xff -.loop: - out (SDC_PORT_SPI), a - nop - djnz .loop - ret - -; Initiate SPI exchange with the SD card. A is the data to send. Received data -; is placed in A. -sdcSendRecv: - out (SDC_PORT_SPI), a - nop - nop - in a, (SDC_PORT_SPI) - nop - nop - ret - -sdcIdle: - ld a, 0xff - jp sdcSendRecv - -; sdcSendRecv 0xff until the response is something else than 0xff for a maximum -; of 20 times. Returns 0xff if no response. -sdcWaitResp: - push bc - ld b, 20 -.loop: - call sdcIdle - inc a ; if 0xff, it's going to become zero - jr nz, .end ; not zero? good, that's our command - djnz .loop -.end: - ; whether we had a success or failure, we return the result. - ; But first, let's bring it back to its original value. - dec a - pop bc - ret - -; The opposite of sdcWaitResp: we wait until response if 0xff. After a -; successful read or write operation, the card will be busy for a while. We need -; to give it time before interacting with it again. Technically, we could -; continue processing on our side while the card it busy, and maybe we will one -; day, but at the moment, I'm having random write errors if I don't do this -; right after a write, so I prefer to stay cautious for now. -; This has no error condition and preserves A -sdcWaitReady: - push af - ; for now, we have no timeout for waiting. It means that broken SD - ; cards can cause infinite loops. -.loop: - call sdcIdle - inc a ; if 0xff, it's going to become zero - jr nz, .loop ; not zero? still busy. loop - pop af - ret - -; Sends a command to the SD card, along with arguments and specified CRC fields. -; (CRC is only needed in initial commands though). -; A: Command to send -; H: Arg 1 (MSB) -; L: Arg 2 -; D: Arg 3 -; E: Arg 4 (LSB) -; -; Returns R1 response in A. -; -; This does *not* handle CS. You have to select/deselect the card outside this -; routine. -sdcCmd: - push bc - - ; Wait until ready to receive commands - push af - call sdcWaitResp - pop af - - ld c, 0 ; init CRC - call .crc7 - call sdcSendRecv - ; Arguments - ld a, h - call .crc7 - call sdcSendRecv - ld a, l - call .crc7 - call sdcSendRecv - ld a, d - call .crc7 - call sdcSendRecv - ld a, e - call .crc7 - call sdcSendRecv - ; send CRC - ld a, c - or 0x01 ; ensure stop bit is set - call sdcSendRecv - - ; And now we just have to wait for a valid response... - call sdcWaitResp - pop bc - ret - -; push A into C and compute CRC7 with 0x09 polynomial -; Note that the result is "left aligned", that is, that 8th bit to the "right" -; is insignificant (will be stop bit). -.crc7: - push af - xor c - ld b, 8 -.loop: - sla a - jr nc, .noCRC - ; msb was set, apply polynomial - xor 0x12 ; 0x09 << 1. We apply CRC on high 7 bits -.noCRC: - djnz .loop - ld c, a - pop af - ret - -; Send a command that expects a R1 response, handling CS. -sdcCmdR1: - out (SDC_PORT_CSLOW), a - call sdcCmd - out (SDC_PORT_CSHIGH), a - ret - -; Send a command that expects a R7 response, handling CS. A R7 is a R1 followed -; by 4 bytes. Those 4 bytes are returned in HL/DE in the same order as in -; sdcCmd. -sdcCmdR7: - out (SDC_PORT_CSLOW), a - call sdcCmd - - ; We have our R1 response in A. Let's try reading the next 4 bytes in - ; case we have a R3. - push af - call sdcIdle - ld h, a - call sdcIdle - ld l, a - call sdcIdle - ld d, a - call sdcIdle - ld e, a - pop af - - out (SDC_PORT_CSHIGH), a - ret - -; Initialize a SD card. This should be called at least 1ms after the powering -; up of the card. Sets result code in A. Zero means success, non-zero means -; error. -sdcInitialize: - push hl - push de - push bc - call sdcWakeUp - - ; Call CMD0 and expect a 0x01 response (card idle) - ; This should be called multiple times. We're actually expected to. - ; Let's call this for a maximum of 10 times. - ld b, 10 -.loop1: - ld a, 0b01000000 ; CMD0 - ld hl, 0 - ld de, 0 - call sdcCmdR1 - cp 0x01 - jp z, .cmd0ok - djnz .loop1 - ; Nothing? error - jr .error -.cmd0ok: - - ; Then comes the CMD8. We send it with a 0x01aa argument and expect - ; a 0x01aa argument back, along with a 0x01 R1 response. - ld a, 0b01001000 ; CMD8 - ld hl, 0 - ld de, 0x01aa - call sdcCmdR7 - cp 0x01 - jr nz, .error - xor a - cp h ; H is zero - jr nz, .error - cp l ; L is zero - jr nz, .error - ld a, d - cp 0x01 - jp nz, .error - ld a, e - cp 0xaa - jr nz, .error - - ; Now we need to repeatedly run CMD55+CMD41 (0x40000000) until we - ; the card goes out of idle mode, that is, when it stops sending us - ; 0x01 response and send us 0x00 instead. Any other response means that - ; initialization failed. -.loop2: - ld a, 0b01110111 ; CMD55 - ld hl, 0 - ld de, 0 - call sdcCmdR1 - cp 0x01 - jr nz, .error - ld a, 0b01101001 ; CMD41 (0x40000000) - ld hl, 0x4000 - ld de, 0x0000 - call sdcCmdR1 - cp 0x01 - jr z, .loop2 - or a ; cp 0 - jr nz, .error - ; Success! out of idle mode! - jr .end - -.error: - ld a, 0x01 -.end: - pop bc - pop de - pop hl - ret - -; Read block index specified in DE and place the contents in buffer pointed to -; by (SDC_BUFPTR). -; If the operation is a success, updates buffer's sector to the value of DE. -; After a block read, check that CRC given by the card matches the content. If -; it doesn't, retries up to SDC_MAXTRIES times. -; Returns 0 in A if success, non-zero if error. -sdcReadBlk: - xor a - ld (SDC_RETRYCNT), a - - push bc - push hl - - out (SDC_PORT_CSLOW), a -.retry: - ld hl, 0 - ; DE already has the correct value - ld a, 0b01010001 ; CMD17 - call sdcCmd - or a ; cp 0 - jr nz, .error - - ; Command sent, no error, now let's wait for our data response. - ld b, 20 -.loop1: - call sdcWaitResp - ; 0xfe is the expected data token for CMD17 - cp 0xfe - jr z, .loop1end - cp 0xff - jr nz, .error - djnz .loop1 - jr .error ; timeout. error out -.loop1end: - ; We received our data token! - ; Data packets follow immediately, we have 512+CRC of them to read - ld bc, SDC_BLKSIZE+2 - ld hl, (SDC_BUFPTR) ; HL --> active buffer's sector - ; It sounds a bit wrong to set bufsec and dirty flag before we get our - ; actual data, but at this point, we don't have any error conditions - ; left, success is guaranteed. To avoid needlesssly INCing hl, let's - ; set sector and dirty along the way - ld (hl), e ; sector number LSB - inc hl - ld (hl), d ; sector number MSB - inc hl ; dirty flag - xor a ; unset - ld (hl), a - inc hl ; actual contents -.loop2: - call sdcIdle - ld (hl), a - cpi ; a trick to inc HL and dec BC at the same time. - ; P/V indicates whether BC reached 0 - jp pe, .loop2 ; BC is not zero, loop - ; Success! while the card is busy, let's get busy too: let's check and - ; see if the CRC matches. - push de ; <| - call sdcCRC ; | - ; before we check the CRC r|esults, let's wait until card is ready - call sdcWaitReady ; | - ; check CRC results | - ld a, (hl) ; | - cp d ; | - jr nz, .crcMismatch ; | - inc hl ; | - ld a, (hl) ; | - cp e ; | - jr nz, .crcMismatch ; | - pop de ; <| - ; Everything is fine and dandy! - xor a ; success - jr .end -.crcMismatch: - ; CRC of the buffer's content doesn't match the CRC reported by the - ; card. Let's retry. - pop de ; from the push right before call sdcCRC - ld a, (SDC_RETRYCNT) - inc a - ld (SDC_RETRYCNT), a - cp SDC_MAXTRIES - jr nz, .retry ; we jump inside our main stack push. No need - ; to pop it. - ; Continue to error condition. Even if we went through many retries, - ; our stack is still the same as it was at the first call. We can return - ; normally (but in error condition). -.error: - ; try to preserve error code - or a ; cp 0 - jr nz, .end ; already non-zero - inc a ; zero, adjust -.end: - out (SDC_PORT_CSHIGH), a - pop hl - pop bc - ret - -; Write the contents of buffer where (SDC_BUFPTR) points to in sector associated -; to it. Unsets the the buffer's dirty flag on success. -; Before writing the block, update the buffer's CRC field so that the correct -; CRC is sent. -; A returns 0 in A on success (with Z set), non-zero (with Z unset) on error. -sdcWriteBlk: - push ix - ld ix, (SDC_BUFPTR) ; IX points to sector LSB - xor a - cp (ix+2) ; dirty flag - pop ix - ret z ; don't write if dirty flag is zero - - push bc - push de - push hl - - call sdcCRC ; DE -> new CRC. HL -> pointer to buf CRC - ld (hl), d ; write computed CRC - inc hl - ld (hl), e - - out (SDC_PORT_CSLOW), a - ld hl, (SDC_BUFPTR) ; sector LSB - ld e, (hl) ; sector LSB - inc hl - ld d, (hl) ; sector MSB - ld hl, 0 ; high addr word always zero, DE already set - ld a, 0b01011000 ; CMD24 - call sdcCmd - or a ; cp 0 - jr nz, .error - - ; Before sending the data packet, we need to send at least one empty - ; byte. - call sdcIdle - - ; data packet token for CMD24 - ld a, 0xfe - call sdcSendRecv - - ; Sending our data token! - ld bc, SDC_BLKSIZE+2 ; +2 for CRC. (as of now, however, that - ; CRC isn't properly updated. Because - ; CMD59 isn't enabled, it doesn't - ; matter) - ld hl, (SDC_BUFPTR) - inc hl ; sector MSB - inc hl ; dirty flag - inc hl ; beginning of contents - -.loop: - ld a, (hl) - call sdcSendRecv - cpi ; a trick to inc HL and dec BC at the same time. - ; P/V indicates whether BC reached 0 - jp pe, .loop ; BC is not zero, loop - ; Let's see what response we have - call sdcWaitResp - and 0b00011111 ; We ignore the first 3 bits of the response. - cp 0b00000101 ; A valid response is "010" in bits 3:1 flanked - ; by 0 on its left and 1 on its right. - jr nz, .error - ; good! Now, we need to let the card process this data. It will return - ; 0xff when it's not busy any more. - call sdcWaitResp - ; Success! Now let's unset the dirty flag - ld hl, (SDC_BUFPTR) - inc hl ; sector MSB - inc hl ; dirty flag - xor a - ld (hl), a - - ; Before returning, wait until card is ready - call sdcWaitReady - xor a - jr .end -.error: - ; try to preserve error code - or a ; cp 0 - jr nz, .end ; already non-zero - inc a ; zero, adjust -.end: - out (SDC_PORT_CSHIGH), a - pop hl - pop de - pop bc - ret - -; Considering the first 15 bits of EHL, select the most appropriate of our two -; buffers and, if necessary, sync that buffer with the SD card. If the selected -; buffer doesn't have the same sector as what EHL asks, load that buffer from -; the SD card. -; If the dirty flag is set, we write the content of the in-memory buffer to the -; SD card before we read a new sector. -; Returns Z on success, not-Z on error (with the error code from either -; sdcReadBlk or sdcWriteBlk) -sdcSync: - push de - ; Given a 24-bit address in EHL, extracts the 15-bit sector from it and - ; place it in DE. - ; We need to shift both E and H right by one bit - srl e ; sets Carry - ld d, e - ld a, h - rra ; takes Carry - ld e, a - - ; Let's first see if our first buffer has our sector - ld a, (SDC_BUFSEC1) ; sector LSB - cp e - jr nz, .notBuf1 - ld a, (SDC_BUFSEC1+1) ; sector MSB - cp d - jr z, .buf1Ok - -.notBuf1: - ; Ok, let's check for buf2 then - ld a, (SDC_BUFSEC2) ; sector LSB - cp e - jr nz, .notBuf2 - ld a, (SDC_BUFSEC2+1) ; sector MSB - cp d - jr z, .buf2Ok - -.notBuf2: - ; None of our two buffers have the sector we need, we'll need to load - ; a new one. - - ; We select our buffer depending on which is dirty. If both are on the - ; same status of dirtiness, we pick any (the first in our case). If one - ; of them is dirty, we pick the clean one. - push de ; <| - ld de, SDC_BUFSEC1 ; | - ld a, (SDC_BUFDIRTY1) ; | - or a ; | is buf1 dirty? - jr z, .ready ; | no? good, that's our buffer - ; yes? then buf2 is our buffer. ; | - ld de, SDC_BUFSEC2 ; | - ; | -.ready: ; | - ; At this point, DE points to one o|f our two buffers, the good one. - ; Let's save it to SDC_BUFPTR | - ld (SDC_BUFPTR), de ; | - ; | - pop de ; <| - - ; We have to read a new sector, but first, let's write the current one - ; if needed. - call sdcWriteBlk - jr nz, .end ; error - ; Let's read our new sector in DE - call sdcReadBlk - jr .end - -.buf1Ok: - ld de, SDC_BUFSEC1 - ld (SDC_BUFPTR), de - ; Z already set - jr .end - -.buf2Ok: - ld de, SDC_BUFSEC2 - ld (SDC_BUFPTR), de - ; Z already set - ; to .end -.end: - pop de - ret - -; Computes the CRC-16, with polynomial 0x1021 of buffer at (SDC_BUFPTR) and -; returns its value in DE. Also, make HL point to the first byte of the CRC -; associated to (SDC_BUFPTR). -sdcCRC: - push af - push bc - ld hl, (SDC_BUFPTR) - inc hl \ inc hl \ inc hl ; HL points to contents - ld bc, SDC_BLKSIZE - ld de, 0 -.loop: - push bc ; <| - ld b, 8 ; | - ld a, (hl) ; | - xor d ; | - ld d, a ; | -.inner: ; | - sla e ; | Sets Carry - rl d ; | Takes and sets carry - jr nc, .noCRC ; | - ; msb was set, apply polyno|mial - ld a, d ; | - xor 0x10 ; | - ld d, a ; | - ld a, e ; | - xor 0x21 ; | - ld e, a ; | -.noCRC: ; | - djnz .inner ; | - pop bc ; <| - - cpi ; inc HL, dec BC, sets P/V on BC=0 - jp pe, .loop ; BC is not zero, loop - ; At this point, HL points to the right place: the first byte of the - ; recorded CRC. - pop bc - pop af - ret - -sdcInitializeCmd: - call sdcInitialize - ret nz - call .setBlkSize - ret nz - call .enableCRC - ret nz - ; At this point, our buffers are unnitialized. We could have some logic - ; that determines whether a buffer is initialized in appropriate SDC - ; routines and act appropriately, but why bother when we could, instead, - ; just buffer the first two sectors of the card on initialization? This - ; way, no need for special conditions. - ; initialize variables - ld hl, SDC_BUFSEC1 - ld (SDC_BUFPTR), hl - ld de, 0 - call sdcReadBlk ; read sector 0 in buf1 - ret nz - ld hl, SDC_BUFSEC2 - ld (SDC_BUFPTR), hl - inc de - jp sdcReadBlk ; read sector 1 in buf2, returns - -; Send a command to set block size to SDC_BLKSIZE to the SD card. -; Returns zero in A if a success, non-zero otherwise -.setBlkSize: - push hl - push de - - ld a, 0b01010000 ; CMD16 - ld hl, 0 - ld de, SDC_BLKSIZE - call sdcCmdR1 - ; Since we're out of idle mode, we expect a 0 response - ; We need no further processing: A is already the correct value. - pop de - pop hl - ret - -; Enable CRC checks through CMD59 -.enableCRC: - push hl - push de - - ld a, 0b01111011 ; CMD59 - ld hl, 0 - ld de, 1 ; 1 means CRC enabled - call sdcCmdR1 - pop de - pop hl - ret - -; Flush the current SDC buffer if dirty -sdcFlushCmd: - ld hl, SDC_BUFSEC1 - ld (SDC_BUFPTR), hl - call sdcWriteBlk - ret nz - ld hl, SDC_BUFSEC2 - ld (SDC_BUFPTR), hl - jp sdcWriteBlk ; returns - -; *** blkdev routines *** - -; Make HL point to its proper place in SDC_BUF. -; EHL currently is a 24-bit offset to read in the SD card. E=high byte, -; HL=low word. Load the proper sector in memory and make HL point to the -; correct data in the memory buffer. -_sdcPlaceBuf: - call sdcSync - ret nz ; error - ; At this point, we have the proper buffer in place and synced in - ; (SDC_BUFPTR). Only the 9 low bits of HL are important. - push de - ld de, (SDC_BUFPTR) - inc de ; sector MSB - inc de ; dirty flag - inc de ; contents - ld a, h ; high byte - and 0x01 ; is first bit set? - jr z, .read ; first bit reset? we're in the "lowbuf" zone. - ; DE already points to the right place. - ; We're in the highbuf zone, let's inc DE by 0x100, which, as it turns - ; out, is quite easy. - inc d -.read: - ; DE is now placed either on the lower or higher half of the active - ; buffer and all we need is to increase DE the lower half of HL. - ld a, l - call addDE - ex de, hl - pop de - ; Now, HL points exactly at the right byte in the active buffer. - xor a ; ensure Z - ret - -sdcGetB: - push hl - call _sdcPlaceBuf - jr nz, .end ; NZ already set - - ; This is it! - ld a, (hl) - cp a ; ensure Z -.end: - pop hl - ret - -sdcPutB: - push hl - push af ; let's remember the char we put, _sdcPlaceBuf - ; destroys A. - call _sdcPlaceBuf - jr nz, .error - - ; HL points to our dest. Recall A and write - pop af - ld (hl), a - - ; Now, let's set the dirty flag - ld a, 1 - ld hl, (SDC_BUFPTR) - inc hl ; sector MSB - inc hl ; point to dirty flag - ld (hl), a ; set dirty flag - xor a ; ensure Z - jr .end -.error: - ; preserve error code - ex af, af' - pop af - ex af, af' - call unsetZ -.end: - pop hl - ret diff --git a/kernel/sms/kbd.asm b/kernel/sms/kbd.asm deleted file mode 100644 index 8a3a178..0000000 --- a/kernel/sms/kbd.asm +++ /dev/null @@ -1,102 +0,0 @@ -; kbd - implement FetchKC for SMS PS/2 adapter -; -; Implements KBD_FETCHKC for the adapter described in recipe sms/kbd. It does -; so for both Port A and Port B (you hook whichever you prefer). - -; FetchKC on Port A -smskbdFetchKCA: - ; Before reading a character, we must first verify that there is - ; something to read. When the adapter is finished filling its '164 up, - ; it resets the latch, which output's is connected to TL. When the '164 - ; is full, TL is low. - ; Port A TL is bit 4 - in a, (0xdc) - and 0b00010000 - jr nz, .nothing - - push bc - in a, (0x3f) - ; Port A TH output, low - ld a, 0b11011101 - out (0x3f), a - nop - nop - in a, (0xdc) - ; bit 3:0 are our dest bits 3:0. handy... - and 0b00001111 - ld b, a - ; Port A TH output, high - ld a, 0b11111101 - out (0x3f), a - nop - nop - in a, (0xdc) - ; bit 3:0 are our dest bits 7:4 - rlca \ rlca \ rlca \ rlca - and 0b11110000 - or b - ex af, af' - ; Port A/B reset - ld a, 0xff - out (0x3f), a - ex af, af' - pop bc - ret - -.nothing: - xor a - ret - -; FetchKC on Port B -smskbdFetchKCB: - ; Port B TL is bit 2 - in a, (0xdd) - and 0b00000100 - jr nz, .nothing - - push bc - in a, (0x3f) - ; Port B TH output, low - ld a, 0b01110111 - out (0x3f), a - nop - nop - in a, (0xdc) - ; bit 7:6 are our dest bits 1:0 - rlca \ rlca - and 0b00000011 - ld b, a - in a, (0xdd) - ; bit 1:0 are our dest bits 3:2 - rlca \ rlca - and 0b00001100 - or b - ld b, a - ; Port B TH output, high - ld a, 0b11110111 - out (0x3f), a - nop - nop - in a, (0xdc) - ; bit 7:6 are our dest bits 5:4 - rrca \ rrca - and 0b00110000 - or b - ld b, a - in a, (0xdd) - ; bit 1:0 are our dest bits 7:6 - rrca \ rrca - and 0b11000000 - or b - ex af, af' - ; Port A/B reset - ld a, 0xff - out (0x3f), a - ex af, af' - pop bc - ret - -.nothing: - xor a - ret - diff --git a/kernel/sms/pad.asm b/kernel/sms/pad.asm deleted file mode 100644 index 4f17cb1..0000000 --- a/kernel/sms/pad.asm +++ /dev/null @@ -1,205 +0,0 @@ -; pad - read input from MD controller -; -; Conveniently expose an API to read the status of a MD pad A. Moreover, -; implement a mechanism to input arbitrary characters from it. It goes as -; follow: -; -; * Direction pad select characters. Up/Down move by one, Left/Right move by 5\ -; * Start acts like Return -; * A acts like Backspace -; * B changes "character class": lowercase, uppercase, numbers, special chars. -; The space character is the first among special chars. -; * C confirms letter selection -; -; This module is currently hard-wired to sms/vdp, that is, it calls vdp's -; routines during padGetC to update character selection. -; -; *** Consts *** -; -.equ PAD_CTLPORT 0x3f -.equ PAD_D1PORT 0xdc - -.equ PAD_UP 0 -.equ PAD_DOWN 1 -.equ PAD_LEFT 2 -.equ PAD_RIGHT 3 -.equ PAD_BUTB 4 -.equ PAD_BUTC 5 -.equ PAD_BUTA 6 -.equ PAD_START 7 - -; *** Variables *** -; -; Button status of last padUpdateSel call. Used for debouncing. -.equ PAD_SELSTAT PAD_RAMSTART -; Current selected character -.equ PAD_SELCHR @+1 -; When non-zero, will be the next char returned in GetC. So far, only used for -; LF that is feeded when Start is pressed. -.equ PAD_NEXTCHR @+1 -.equ PAD_RAMEND @+1 - -; *** Code *** - -padInit: - ld a, 0xff - ld (PAD_SELSTAT), a - xor a - ld (PAD_NEXTCHR), a - ld a, 'a' - ld (PAD_SELCHR), a - ret - -; Put status for port A in register A. Bits, from MSB to LSB: -; Start - A - C - B - Right - Left - Down - Up -; Each bit is high when button is unpressed and low if button is pressed. For -; example, when no button is pressed, 0xff is returned. -padStatus: - ; This logic below is for the Genesis controller, which is modal. TH is - ; an output pin that swiches the meaning of TL and TR. When TH is high - ; (unselected), TL = Button B and TR = Button C. When TH is low - ; (selected), TL = Button A and TR = Start. - push bc - ld a, 0b11111101 ; TH output, unselected - out (PAD_CTLPORT), a - in a, (PAD_D1PORT) - and 0x3f ; low 6 bits are good - ld b, a ; let's store them - ; Start and A are returned when TH is selected, in bits 5 and 4. Well - ; get them, left-shift them and integrate them to B. - ld a, 0b11011101 ; TH output, selected - out (PAD_CTLPORT), a - in a, (PAD_D1PORT) - and 0b00110000 - sla a - sla a - or b - pop bc - ret - -; From a pad status in A, update current char selection and return it. -; Sets Z if current selection was unchanged, unset if changed. -padUpdateSel: - call padStatus - push hl ; --> lvl 1 - ld hl, PAD_SELSTAT - cp (hl) - ld (hl), a - pop hl ; <-- lvl 1 - jr z, .nothing ; nothing changed - bit PAD_UP, a - jr z, .up - bit PAD_DOWN, a - jr z, .down - bit PAD_LEFT, a - jr z, .left - bit PAD_RIGHT, a - jr z, .right - bit PAD_BUTB, a - jr z, .nextclass - jr .nothing -.up: - ld a, (PAD_SELCHR) - inc a - jr .setchr -.down: - ld a, (PAD_SELCHR) - dec a - jr .setchr -.left: - ld a, (PAD_SELCHR) - dec a \ dec a \ dec a \ dec a \ dec a - jr .setchr -.right: - ld a, (PAD_SELCHR) - inc a \ inc a \ inc a \ inc a \ inc a - jr .setchr -.nextclass: - ; Go to the beginning of the next "class" of characters - push bc - ld a, (PAD_SELCHR) - ld b, '0' - cp b - jr c, .setclass ; A < '0' - ld b, ':' - cp b - jr c, .setclass - ld b, 'A' - cp b - jr c, .setclass - ld b, '[' - cp b - jr c, .setclass - ld b, 'a' - cp b - jr c, .setclass - ld b, ' ' - ; continue to .setclass -.setclass: - ld a, b - pop bc - ; continue to .setchr -.setchr: - ; check range first - cp 0x7f - jr nc, .tooHigh - cp 0x20 - jr nc, .setchrEnd ; not too low - ; too low, probably because we overdecreased. Let's roll over - ld a, '~' - jr .setchrEnd -.tooHigh: - ; too high, probably because we overincreased. Let's roll over - ld a, ' ' - ; continue to .setchrEnd -.setchrEnd: - ld (PAD_SELCHR), a - jp unsetZ -.nothing: - ; Z already set - ld a, (PAD_SELCHR) - ret - -; Repeatedly poll the pad for input and returns the resulting "input char". -; This routine takes a long time to return because it waits until C, B or Start -; was pressed. Until this is done, this routine takes care of updating the -; "current selection" directly in the VDP. -padGetC: - ld a, (PAD_NEXTCHR) - or a - jr nz, .nextchr - call padUpdateSel - jp z, padGetC ; nothing changed, loop - ; pad status was changed, let's see if an action button was pressed - ld a, (PAD_SELSTAT) - bit PAD_BUTC, a - jr z, .advance - bit PAD_BUTA, a - jr z, .backspace - bit PAD_START, a - jr z, .return - ; no action button pressed, but because our pad status changed, update - ; VDP before looping. - ld a, (PAD_SELCHR) - call gridSetCurH - jp padGetC -.return: - ld a, LF - ld (PAD_NEXTCHR), a - ; continue to .advance -.advance: - ld a, (PAD_SELCHR) - ; Z was already set from previous BIT instruction - jp gridSetCurL -.backspace: - ld a, BS - ; Z was already set from previous BIT instruction - ret -.nextchr: - ; We have a "next char", return it and clear it. - cp a ; ensure Z - ex af, af' - xor a - ld (PAD_NEXTCHR), a - ex af, af' - ret diff --git a/kernel/sms/vdp.asm b/kernel/sms/vdp.asm deleted file mode 100644 index ca30ce2..0000000 --- a/kernel/sms/vdp.asm +++ /dev/null @@ -1,174 +0,0 @@ -; vdp - console on SMS' VDP -; -; Implement PutC on the console. Characters start at the top left. Every PutC -; call converts the ASCII char received to its internal font, then put that -; char on screen, advancing the cursor by one. When reaching the end of the -; line (33rd char), wrap to the next. -; -; In the future, there's going to be a scrolling mechanism when we reach the -; bottom of the screen, but for now, when the end of the screen is reached, we -; wrap up to the top. -; -; When reaching a new line, we clear that line and the next to help readability. -; -; *** Defines *** -; FNT_DATA: Pointer to 7x7 font data. -; *** Consts *** -; -.equ VDP_CTLPORT 0xbf -.equ VDP_DATAPORT 0xbe -.equ VDP_COLS 32 -.equ VDP_ROWS 24 - -; *** Code *** - -vdpInit: - ld hl, .initData - ld b, .initDataEnd-.initData - ld c, VDP_CTLPORT - otir - - ; Blank VRAM - xor a - out (VDP_CTLPORT), a - ld a, 0x40 - out (VDP_CTLPORT), a - ld bc, 0x4000 -.loop1: - xor a - out (VDP_DATAPORT), a - dec bc - ld a, b - or c - jr nz, .loop1 - - ; Set palettes - xor a - out (VDP_CTLPORT), a - ld a, 0xc0 - out (VDP_CTLPORT), a - ld hl, .paletteData - ld b, .paletteDataEnd-.paletteData - ld c, VDP_DATAPORT - otir - - ; Define tiles - xor a - out (VDP_CTLPORT), a - ld a, 0x40 - out (VDP_CTLPORT), a - ld hl, FNT_DATA - ld c, 0x7e-0x20 ; range of displayable chars in font. - ; Each row in FNT_DATA is a row of the glyph and there is 7 of them. - ; We insert a blank one at the end of those 7. For each row we set, we - ; need to send 3 zero-bytes because each pixel in the tile is actually - ; 4 bits because it can select among 16 palettes. We use only 2 of them, - ; which is why those bytes always stay zero. -.loop2: - ld b, 7 -.loop3: - ld a, (hl) - out (VDP_DATAPORT), a - ; send 3 blanks - xor a - out (VDP_DATAPORT), a - nop ; the VDP needs 16 T-states to breathe - out (VDP_DATAPORT), a - nop - out (VDP_DATAPORT), a - inc hl - djnz .loop3 - ; Send a blank row after the 7th row - xor a - out (VDP_DATAPORT), a - nop - out (VDP_DATAPORT), a - nop - out (VDP_DATAPORT), a - nop - out (VDP_DATAPORT), a - dec c - jr nz, .loop2 - - ; Bit 7 = ?, Bit 6 = display enabled - ld a, 0b11000000 - out (VDP_CTLPORT), a - ld a, 0x81 - out (VDP_CTLPORT), a - ret - -; VDP initialisation data -.initData: -; 0x8x == set register X -.db 0b00000100, 0x80 ; Bit 2: Select mode 4 -.db 0b00000000, 0x81 -.db 0b11111111, 0x82 ; Name table: 0x3800 -.db 0b11111111, 0x85 ; Sprite table: 0x3f00 -.db 0b11111111, 0x86 ; sprite use tiles from 0x2000 -.db 0b11111111, 0x87 ; Border uses palette 0xf -.db 0b00000000, 0x88 ; BG X scroll -.db 0b00000000, 0x89 ; BG Y scroll -.db 0b11111111, 0x8a ; Line counter (why have this?) -.initDataEnd: -.paletteData: -; BG palette -.db 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -; Sprite palette (inverted colors) -.db 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -.db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -.paletteDataEnd: - -; Convert ASCII char in A into a tile index corresponding to that character. -; When a character is unknown, returns 0x5e (a '~' char). -vdpConv: - ; The font is organized to closely match ASCII, so this is rather easy. - ; We simply subtract 0x20 from incoming A - sub 0x20 - cp 0x5f - ret c ; A < 0x5f, good - ld a, 0x5e - ret - -; grid routine. Sets cell at row D and column E to character A. If C is one, we -; use the sprite palette. -vdpSetCell: - call vdpConv - ; store A away - ex af, af' - push bc - ld b, 0 ; we push rotated bits from D into B so - ; that we'll already have our low bits from the - ; second byte we'll send right after. - ; Here, we're fitting a 5-bit line, and a 5-bit column on 16-bit, right - ; aligned. On top of that, our righmost bit is taken because our target - ; cell is 2-bytes wide and our final number is a VRAM address. - ld a, d - sla a ; should always push 0, so no pushing in B - sla a ; same - sla a ; same - sla a \ rl b - sla a \ rl b - sla a \ rl b - ld c, a - ld a, e - sla a ; A * 2 - or c ; bring in two low bits from D into high - ; two bits - out (VDP_CTLPORT), a - ld a, b ; 3 low bits set - or 0x78 ; 01 header + 0x3800 - out (VDP_CTLPORT), a - pop bc - - ; We're ready to send our data now. Let's go - ex af, af' - out (VDP_DATAPORT), a - - ; Palette select is on bit 3 of MSB - ld a, 1 - and c - rla \ rla \ rla - out (VDP_DATAPORT), a - ret - diff --git a/kernel/stdio.asm b/kernel/stdio.asm deleted file mode 100644 index 231fbd5..0000000 --- a/kernel/stdio.asm +++ /dev/null @@ -1,148 +0,0 @@ -; stdio -; -; Allows other modules to print to "standard out", and get data from "standard -; in", that is, the console through which the user is connected in a decoupled -; manner. -; -; Those GetC/PutC routines are hooked through defines and have this API: -; -; GetC: Blocks until a character is read from the device and return that -; character in A. -; -; PutC: Write character specified in A onto the device. -; -; *** Accepted characters *** -; -; For now, we're in muddy waters in this regard. We try to stay close to ASCII. -; Anything over 0x7f is undefined. Both CR and LF are interpreted as "line end". -; Both BS and DEL mean "delete previous character". -; -; When outputting, newlines are marked by CR and LF. Outputting a character -; deletion is made through BS then space then BS. -; -; *** Defines *** -; STDIO_GETC: address of a GetC routine -; STDIO_PUTC: address of a PutC routine -; -; *** Consts *** -; Size of the readline buffer. If a typed line reaches this size, the line is -; flushed immediately (same as pressing return). -.equ STDIO_BUFSIZE 0x40 - -; *** Variables *** -; Line buffer. We read types chars into this buffer until return is pressed -; This buffer is null-terminated. -.equ STDIO_BUF STDIO_RAMSTART - -; Index where the next char will go in stdioGetC. -.equ STDIO_RAMEND @+STDIO_BUFSIZE - -stdioGetC: - jp STDIO_GETC - -stdioPutC: - jp STDIO_PUTC - -; print null-terminated string pointed to by HL -printstr: - push af - push hl - -.loop: - ld a, (hl) ; load character to send - or a ; is it zero? - jr z, .end ; if yes, we're finished - call STDIO_PUTC - inc hl - jr .loop - -.end: - pop hl - pop af - ret - -; print B characters from string that HL points to -printnstr: - push bc - push hl -.loop: - ld a, (hl) ; load character to send - call STDIO_PUTC - inc hl - djnz .loop - -.end: - pop hl - pop bc - ret - -; Prints a line terminator. This routine is a bit of a misnomer because it's -; designed to be overridable to, for example, printlf, but we'll live with it -; for now... -printcrlf: - push af - ld a, CR - call STDIO_PUTC - ld a, LF - call STDIO_PUTC - pop af - ret - -; Repeatedly calls stdioGetC until a whole line was read, that is, when CR or -; LF is read or if the buffer is full. Sets HL to the beginning of the read -; line, which is null-terminated. -; -; This routine also takes care of echoing received characters back to the TTY. -; It also manages backspaces properly. -stdioReadLine: - push bc - ld hl, STDIO_BUF - ld b, STDIO_BUFSIZE-1 -.loop: - ; Let's wait until something is typed. - call STDIO_GETC - ; got it. Now, is it a CR or LF? - cp CR - jr z, .complete ; char is CR? buffer complete! - cp LF - jr z, .complete - cp DEL - jr z, .delchr - cp BS - jr z, .delchr - - ; Echo the received character right away so that we see what we type - call STDIO_PUTC - - ; Ok, gotta add it do the buffer - ld (hl), a - inc hl - djnz .loop - ; buffer overflow, complete line -.complete: - ; The line in our buffer is complete. - ; Let's null-terminate it and return. - xor a - ld (hl), a - ld hl, STDIO_BUF - pop bc - ret - -.delchr: - ; Deleting is a tricky business. We have to decrease HL and increase B - ; so that everything stays consistent. We also have to make sure that - ; We don't do buffer underflows. - ld a, b - cp STDIO_BUFSIZE-1 - jr z, .loop ; beginning of line, nothing to delete - dec hl - inc b - ; Char deleted in buffer, now send BS + space + BS for the terminal - ; to clear its previous char - ld a, BS - call STDIO_PUTC - ld a, ' ' - call STDIO_PUTC - ld a, BS - call STDIO_PUTC - jr .loop diff --git a/kernel/str.asm b/kernel/str.asm deleted file mode 100644 index 1ee829b..0000000 --- a/kernel/str.asm +++ /dev/null @@ -1,85 +0,0 @@ -; Fill B bytes at (HL) with A -fill: - push bc - push hl -.loop: - ld (hl), a - inc hl - djnz .loop - pop hl - pop bc - ret - -; Increase HL until the memory address it points to is equal to A for a maximum -; of 0xff bytes. Returns the new HL value as well as the number of bytes -; iterated in A. -; If a null char is encountered before we find A, processing is stopped in the -; same way as if we found our char (so, we look for A *or* 0) -; Set Z if the character is found. Unsets it if not -findchar: - push bc - ld c, a ; let's use C as our cp target - ld b, 0xff - -.loop: ld a, (hl) - cp c - jr z, .match - or a ; cp 0 - jr z, .nomatch - inc hl - djnz .loop -.nomatch: - inc a ; unset Z - jr .end -.match: - ; We ran 0xff-B loops. That's the result that goes in A. - ld a, 0xff - sub b - cp a ; ensure Z -.end: - pop bc - ret - - -; Compares strings pointed to by HL and DE up to A count of characters. If -; equal, Z is set. If not equal, Z is reset. -strncmp: - push bc - push hl - push de - - ld b, a -.loop: - ld a, (de) - cp (hl) - jr nz, .end ; not equal? break early. NZ is carried out - ; to the called - cp 0 ; If our chars are null, stop the cmp - jr z, .end ; The positive result will be carried to the - ; caller - inc hl - inc de - djnz .loop - ; We went through all chars with success, but our current Z flag is - ; unset because of the cp 0. Let's do a dummy CP to set the Z flag. - cp a - -.end: - pop de - pop hl - pop bc - ; Because we don't call anything else than CP that modify the Z flag, - ; our Z value will be that of the last cp (reset if we broke the loop - ; early, set otherwise) - ret - -; Transforms the character in A, if it's in the a-z range, into its upcase -; version. -upcase: - cp 'a' - ret c ; A < 'a'. nothing to do - cp 'z'+1 - ret nc ; A >= 'z'+1. nothing to do - ; 'a' - 'A' == 0x20 - sub 0x20 - ret diff --git a/kernel/ti/kbd.asm b/kernel/ti/kbd.asm deleted file mode 100644 index 2d4f519..0000000 --- a/kernel/ti/kbd.asm +++ /dev/null @@ -1,175 +0,0 @@ -; kbd -; -; Control TI-84+'s keyboard. -; -; *** Constants *** -.equ KBD_PORT 0x01 - -; Keys that have a special meaning in GetC. All >= 0x80. They are interpreted -; by GetC directly and are never returned as-is. -.equ KBD_KEY_ALPHA 0x80 -.equ KBD_KEY_2ND 0x81 - -; *** Variables *** -; active long-term modifiers, such as a-lock -; bit 0: A-Lock -.equ KBD_MODS KBD_RAMSTART -.equ KBD_RAMEND @+1 - -; *** Code *** - -kbdInit: - ld a, 1 ; begin with A-Lock on - ld (KBD_MODS), a - ret - -; Wait for a digit to be pressed and sets the A register ASCII value -; corresponding to that key press. -; -; This routine waits for a key to be pressed, but before that, it waits for -; all keys to be de-pressed. It does that to ensure that two calls to -; waitForKey only go through after two actual key presses (otherwise, the user -; doesn't have enough time to de-press the button before the next waitForKey -; routine registers the same key press as a second one). -; -; Sending 0xff to the port resets the keyboard, and then we have to send groups -; we want to "listen" to, with a 0 in the group bit. Thus, to know if *any* key -; is pressed, we send 0xff to reset the keypad, then 0x00 to select all groups, -; if the result isn't 0xff, at least one key is pressed. -kbdGetC: - push bc - push hl - - ; During this GetC loop, register C holds the modificators - ; bit 0: Alpha - ; bit 1: 2nd - ; Initial value should be zero, but if A-Lock is on, it's 1 - ld a, (KBD_MODS) - and 1 - ld c, a - - ; loop until a digit is pressed -.loop: - ld hl, .dtbl - ; we go through the 7 rows of the table - ld b, 7 - ; is alpha mod enabled? - bit 0, c - jr z, .inner ; unset? skip next - ld hl, .atbl ; set? we're in alpha mode -.inner: - ld a, (hl) ; group mask - call .get - cp 0xff - jr nz, .something - ; nothing for that group, let's scan the next group - ld a, 9 - call addHL ; go to next row - djnz .inner - ; found nothing, loop - jr .loop -.something: - ; We have something on that row! Let's find out which char. Register A - ; currently contains a mask with the pressed char bit unset. - ld b, 8 - inc hl -.findchar: - rrca ; is next bit unset? - jr nc, .gotit ; yes? we have our char! - inc hl - djnz .findchar -.gotit: - ld a, (hl) - or a ; is char 0? - jr z, .loop ; yes? unsupported. loop. - call .debounce - cp KBD_KEY_ALPHA - jr c, .result ; A < 0x80? valid char, return it. - jr z, .handleAlpha - cp KBD_KEY_2ND - jr z, .handle2nd - jp .loop -.handleAlpha: - ; Toggle Alpha bit in C. Also, if 2ND bit is set, toggle A-Lock mod. - ld a, 1 ; mask for Alpha - xor c - ld c, a - bit 1, c ; 2nd set? - jp z, .loop ; unset? loop - ; we've just hit Alpha with 2nd set. Toggle A-Lock and set Alpha to - ; the value A-Lock has. - ld a, (KBD_MODS) - xor 1 - ld (KBD_MODS), a - ld c, a - jp .loop -.handle2nd: - ; toggle 2ND bit in C - ld a, 2 ; mask for 2ND - xor c - ld c, a - jp .loop - -.result: - ; We have our result in A, *almost* time to return it. One last thing: - ; Are in in both Alpha and 2nd mode? If yes, then it means that we - ; should return the upcase version of our letter (if it's a letter). - bit 0, c - jr z, .end ; nope - bit 1, c - jr z, .end ; nope - ; yup, we have Alpha + 2nd. Upcase! - call upcase -.end: - pop hl - pop bc - ret -.get: - ex af, af' - ld a, 0xff - di - out (KBD_PORT), a - ex af, af' - out (KBD_PORT), a - in a, (KBD_PORT) - ei - ret -.debounce: - ; wait until all keys are de-pressed - ; To avoid repeat keys, we require 64 subsequent polls to indicate all - ; depressed keys - push af ; --> lvl 1 - push bc ; --> lvl 2 -.pressed: - ld b, 64 -.wait: - xor a - call .get - inc a ; if a was 0xff, will become 0 (nz test) - jr nz, .pressed ; non-zero? something is pressed - djnz .wait - - pop bc ; <-- lvl 2 - pop af ; <-- lvl 1 - ret - -; digits table. each row represents a group. first item is group mask. -; 0 means unsupported. no group 7 because it has no keys. -.dtbl: - .db 0xfe, 0, 0, 0, 0, 0, 0, 0, 0 - .db 0xfd, 0x0d, '+' ,'-' ,'*', '/', '^', 0, 0 - .db 0xfb, 0, '3', '6', '9', ')', 0, 0, 0 - .db 0xf7, '.', '2', '5', '8', '(', 0, 0, 0 - .db 0xef, '0', '1', '4', '7', ',', 0, 0, 0 - .db 0xdf, 0, 0, 0, 0, 0, 0, 0, KBD_KEY_ALPHA - .db 0xbf, 0, 0, 0, 0, 0, KBD_KEY_2ND, 0, 0x7f - -; alpha table. same as .dtbl, for when we're in alpha mode. -.atbl: - .db 0xfe, 0, 0, 0, 0, 0, 0, 0, 0 - .db 0xfd, 0x0d, '"' ,'w' ,'r', 'm', 'h', 0, 0 - .db 0xfb, '?', 0, 'v', 'q', 'l', 'g', 0, 0 - .db 0xf7, ':', 'z', 'u', 'p', 'k', 'f', 'c', 0 - .db 0xef, ' ', 'y', 't', 'o', 'j', 'e', 'b', 0 - .db 0xdf, 0, 'x', 's', 'n', 'i', 'd', 'a', KBD_KEY_ALPHA - .db 0xbf, 0, 0, 0, 0, 0, KBD_KEY_2ND, 0, 0x7f diff --git a/kernel/ti/lcd.asm b/kernel/ti/lcd.asm deleted file mode 100644 index 3973b45..0000000 --- a/kernel/ti/lcd.asm +++ /dev/null @@ -1,350 +0,0 @@ -; lcd -; -; Implement PutC on TI-84+ (for now)'s LCD screen. -; -; The screen is 96x64 pixels. The 64 rows are addressed directly with CMD_ROW -; but columns are addressed in chunks of 6 or 8 bits (there are two modes). -; -; In 6-bit mode, there are 16 visible columns. In 8-bit mode, there are 12. -; -; Note that "X-increment" and "Y-increment" work in the opposite way than what -; most people expect. Y moves left and right, X moves up and down. -; -; *** Z-Offset *** -; -; This LCD has a "Z-Offset" parameter, allowing to offset rows on the -; screen however we wish. This is handy because it allows us to scroll more -; efficiently. Instead of having to copy the LCD ram around at each linefeed -; (or instead of having to maintain an in-memory buffer), we can use this -; feature. -; -; The Z-Offet goes upwards, with wrapping. For example, if we have an 8 pixels -; high line at row 0 and if our offset is 8, that line will go up 8 pixels, -; wrapping itself to the bottom of the screen. -; -; The principle is this: The active line is always the bottom one. Therefore, -; when active row is 0, Z is FNT_HEIGHT+1, when row is 1, Z is (FNT_HEIGHT+1)*2, -; When row is 8, Z is 0. -; -; *** 6/8 bit columns and smaller fonts *** -; -; If your glyphs, including padding, are 6 or 8 pixels wide, you're in luck -; because pushing them to the LCD can be done in a very efficient manner. -; Unfortunately, this makes the LCD unsuitable for a Collapse OS shell: 6 -; pixels per glyph gives us only 16 characters per line, which is hardly -; usable. -; -; This is why we have this buffering system. How it works is that we're always -; in 8-bit mode and we hold the whole area (8 pixels wide by FNT_HEIGHT high) -; in memory. When we want to put a glyph to screen, we first read the contents -; of that area, then add our new glyph, offsetted and masked, to that buffer, -; then push the buffer back to the LCD. If the glyph is split, move to the next -; area and finish the job. -; -; That being said, it's important to define clearly what CURX and CURY variable -; mean. Those variable keep track of the current position *in pixels*, in both -; axes. -; -; *** Requirements *** -; fnt/mgm -; -; *** Constants *** -.equ LCD_PORT_CMD 0x10 -.equ LCD_PORT_DATA 0x11 - -.equ LCD_CMD_6BIT 0x00 -.equ LCD_CMD_8BIT 0x01 -.equ LCD_CMD_DISABLE 0x02 -.equ LCD_CMD_ENABLE 0x03 -.equ LCD_CMD_XDEC 0x04 -.equ LCD_CMD_XINC 0x05 -.equ LCD_CMD_YDEC 0x06 -.equ LCD_CMD_YINC 0x07 -.equ LCD_CMD_COL 0x20 -.equ LCD_CMD_ZOFFSET 0x40 -.equ LCD_CMD_ROW 0x80 -.equ LCD_CMD_CONTRAST 0xc0 - -; *** Variables *** -; Current Y position on the LCD, that is, where re're going to spit our next -; glyph. -.equ LCD_CURY LCD_RAMSTART -; Current X position -.equ LCD_CURX @+1 -; two pixel buffers that are 8 pixels wide (1b) by FNT_HEIGHT pixels high. -; This is where we compose our resulting pixels blocks when spitting a glyph. -.equ LCD_BUF @+1 -.equ LCD_RAMEND @+FNT_HEIGHT*2 - -; *** Code *** -lcdInit: - ; Initialize variables - xor a - ld (LCD_CURY), a - ld (LCD_CURX), a - - ; Clear screen - call lcdClrScr - - ; We begin with a Z offset of FNT_HEIGHT+1 - ld a, LCD_CMD_ZOFFSET+FNT_HEIGHT+1 - call lcdCmd - - ; Enable the LCD - ld a, LCD_CMD_ENABLE - call lcdCmd - - ; Hack to get LCD to work. According to WikiTI, we're not sure why TIOS - ; sends these, but it sends it, and it is required to make the LCD - ; work. So... - ld a, 0x17 - call lcdCmd - ld a, 0x0b - call lcdCmd - - ; Set some usable contrast - ld a, LCD_CMD_CONTRAST+0x34 - call lcdCmd - - ; Enable 8-bit mode. - ld a, LCD_CMD_8BIT - call lcdCmd - - ret - -; Wait until the lcd is ready to receive a command -lcdWait: - push af -.loop: - in a, (LCD_PORT_CMD) - ; When 7th bit is cleared, we can send a new command - rla - jr c, .loop - pop af - ret - -; Send cmd A to LCD -lcdCmd: - out (LCD_PORT_CMD), a - jr lcdWait - -; Send data A to LCD -lcdDataSet: - out (LCD_PORT_DATA), a - jr lcdWait - -; Get data from LCD into A -lcdDataGet: - in a, (LCD_PORT_DATA) - jr lcdWait - -; Turn LCD off -lcdOff: - push af - ld a, LCD_CMD_DISABLE - call lcdCmd - out (LCD_PORT_CMD), a - pop af - ret - -; Set LCD's current column to A -lcdSetCol: - push af - ; The col index specified in A is compounded with LCD_CMD_COL - add a, LCD_CMD_COL - call lcdCmd - pop af - ret - -; Set LCD's current row to A -lcdSetRow: - push af - ; The col index specified in A is compounded with LCD_CMD_COL - add a, LCD_CMD_ROW - call lcdCmd - pop af - ret - -; Send the glyph that HL points to to the LCD, at its current position. -; After having called this, the LCD's position will have advanced by one -; position -lcdSendGlyph: - push af - push bc - push hl - push ix - - ld a, (LCD_CURY) - call lcdSetRow - ld a, (LCD_CURX) - srl a \ srl a \ srl a ; div by 8 - call lcdSetCol - - ; First operation: read the LCD memory for the "left" side of the - ; buffer. We assume the right side to always be empty, so we don't - ; read it. After having read each line, compose it with glyph line at - ; HL - - ; Before we start, what is our bit offset? - ld a, (LCD_CURX) - and 0b111 - ; that's our offset, store it in C - ld c, a - - ld a, LCD_CMD_XINC - call lcdCmd - ld ix, LCD_BUF - ld b, FNT_HEIGHT - ; A dummy read is needed after a movement. - call lcdDataGet -.loop1: - ; let's go get that glyph data - ld a, (hl) - ld (ix), a - call .shiftIX - ; now let's go get existing pixel on LCD - call lcdDataGet - ; and now let's do some compositing! - or (ix) - ld (ix), a - inc hl - inc ix - djnz .loop1 - - ; Buffer set! now let's send it. - ld a, (LCD_CURY) - call lcdSetRow - - ld hl, LCD_BUF - ld b, FNT_HEIGHT -.loop2: - ld a, (hl) - call lcdDataSet - inc hl - djnz .loop2 - - ; And finally, let's send the "right side" of the buffer - ld a, (LCD_CURY) - call lcdSetRow - ld a, (LCD_CURX) - srl a \ srl a \ srl a ; div by 8 - inc a - call lcdSetCol - - ld hl, LCD_BUF+FNT_HEIGHT - ld b, FNT_HEIGHT -.loop3: - ld a, (hl) - call lcdDataSet - inc hl - djnz .loop3 - - ; Increase column and wrap if necessary - ld a, (LCD_CURX) - add a, FNT_WIDTH+1 - ld (LCD_CURX), a - cp 96-FNT_WIDTH - jr c, .skip ; A < 96-FNT_WIDTH - call lcdLinefeed -.skip: - pop ix - pop hl - pop bc - pop af - ret -; Shift glyph in (IX) to the right C times, sending carry into (IX+FNT_HEIGHT) -.shiftIX: - dec c \ inc c - ret z ; zero? nothing to do - push bc ; --> lvl 1 - xor a - ld (ix+FNT_HEIGHT), a -.shiftLoop: - srl (ix) - rr (ix+FNT_HEIGHT) - dec c - jr nz, .shiftLoop - pop bc ; <-- lvl 1 - ret - -; Changes the current line and go back to leftmost column -lcdLinefeed: - push af - ld a, (LCD_CURY) - call .addFntH - ld (LCD_CURY), a - call lcdClrLn - ; Now, lets set Z offset which is CURROW+FNT_HEIGHT+1 - call .addFntH - add a, LCD_CMD_ZOFFSET - call lcdCmd - xor a - ld (LCD_CURX), a - pop af - ret -.addFntH: - add a, FNT_HEIGHT+1 - cp 64 - ret c ; A < 64? no wrap - ; we have to wrap around - xor a - ret - -; Clears B rows starting at row A -; B is not preserved by this routine -lcdClrX: - push af - call lcdSetRow -.outer: - push bc ; --> lvl 1 - ld b, 11 - ld a, LCD_CMD_YINC - call lcdCmd - xor a - call lcdSetCol -.inner: - call lcdDataSet - djnz .inner - ld a, LCD_CMD_XINC - call lcdCmd - xor a - call lcdDataSet - pop bc ; <-- lvl 1 - djnz .outer - pop af - ret - -lcdClrLn: - push bc - ld b, FNT_HEIGHT+1 - call lcdClrX - pop bc - ret - -lcdClrScr: - push bc - ld b, 64 - call lcdClrX - pop bc - ret - -lcdPutC: - cp LF - jp z, lcdLinefeed - cp BS - jr z, .bs - push hl - call fntGet - jr nz, .end - call lcdSendGlyph -.end: - pop hl - ret -.bs: - ld a, (LCD_CURX) - or a - ret z ; going back one line is too complicated. - ; not implemented yet - sub FNT_WIDTH+1 - ld (LCD_CURX), a - ret diff --git a/kernel/trs80/floppy.asm b/kernel/trs80/floppy.asm deleted file mode 100644 index 83a305c..0000000 --- a/kernel/trs80/floppy.asm +++ /dev/null @@ -1,291 +0,0 @@ -; floppy -; -; Implement a block device around a TRS-80 floppy. It uses SVCs supplied by -; TRS-DOS to do so. -; -; *** Floppy buffers *** -; -; The dual-buffer system is exactly the same as in the "sdc" module. See -; comments there. -; -; *** Consts *** -; Number of sector per cylinder. We only support single density for now. -.equ FLOPPY_SEC_PER_CYL 10 -.equ FLOPPY_MAX_CYL 40 -.equ FLOPPY_BLKSIZE 256 - -; *** Variables *** -; This is a pointer to the currently selected buffer. This points to the BUFSEC -; part, that is, two bytes before actual content begins. -.equ FLOPPY_BUFPTR FLOPPY_RAMSTART -; Sector number currently in FLOPPY_BUF1. Little endian like any other z80 word. -.equ FLOPPY_BUFSEC1 @+2 -; Whether the buffer has been written to. 0 means clean. 1 means dirty. -.equ FLOPPY_BUFDIRTY1 @+2 -; The contents of the buffer. -.equ FLOPPY_BUF1 @+1 - -; second buffer has the same structure as the first. -.equ FLOPPY_BUFSEC2 @+FLOPPY_BLKSIZE -.equ FLOPPY_BUFDIRTY2 @+2 -.equ FLOPPY_BUF2 @+1 -.equ FLOPPY_RAMEND @+FLOPPY_BLKSIZE - -; *** Code *** -floppyInit: - ; Make sure that both buffers are flagged as invalid and not dirty - xor a - ld (FLOPPY_BUFDIRTY1), a - ld (FLOPPY_BUFDIRTY2), a - dec a - ld (FLOPPY_BUFSEC1), a - ld (FLOPPY_BUFSEC2), a - ret - -; Returns whether D (cylinder) and E (sector) are in proper range. -; Z for success. -_floppyInRange: - ld a, e - cp FLOPPY_SEC_PER_CYL - jp nc, unsetZ - ld a, d - cp FLOPPY_MAX_CYL - jp nc, unsetZ - xor a ; set Z - ret - -; Read sector index specified in E and cylinder specified in D and place the -; contents in buffer pointed to by (FLOPPY_BUFPTR). -; If the operation is a success, updates buffer's sector to the value of DE. -; Z on success -floppyRdSec: - call _floppyInRange - ret nz - - push bc - push hl - - ld a, 0x28 ; @DCSTAT - ld c, 1 ; hardcoded to drive :1 for now - rst 0x28 - jr nz, .end - - ld hl, (FLOPPY_BUFPTR) ; HL --> active buffer's sector - ld (hl), e ; sector - inc hl - ld (hl), d ; cylinder - inc hl ; dirty - inc hl ; data - ld a, 0x31 ; @RDSEC - rst 0x28 ; sets proper Z -.end: - pop hl - pop bc - ret - -; Write the contents of buffer where (FLOPPY_BUFPTR) points to in sector -; associated to it. Unsets the the buffer's dirty flag on success. -; Z on success -floppyWrSec: - push ix - ld ix, (FLOPPY_BUFPTR) ; IX points to sector - xor a - cp (ix+2) ; dirty flag - pop ix - ret z ; don't write if dirty flag is zero - - push hl - push de - push bc - ld hl, (FLOPPY_BUFPTR) ; sector - ld e, (hl) - inc hl ; cylinder - ld d, (hl) - call _floppyInRange - jr nz, .end - ld c, 1 ; drive - ld a, 0x28 ; @DCSTAT - rst 0x28 - jr nz, .end - inc hl ; dirty - xor a - ld (hl), a ; undirty the buffer - inc hl ; data - ld a, 0x35 ; @WRSEC - rst 0x28 ; sets proper Z -.end: - pop bc - pop de - pop hl - ret - -; Considering the first 15 bits of EHL, select the most appropriate of our two -; buffers and, if necessary, sync that buffer with the floppy. If the selected -; buffer doesn't have the same sector as what EHL asks, load that buffer from -; the floppy. -; If the dirty flag is set, we write the content of the in-memory buffer to the -; floppy before we read a new sector. -; Returns Z on success, NZ on error -floppySync: - push de - ; Given a 24-bit address in EHL, extracts the 16-bit sector from it and - ; place it in DE, following cylinder and sector rules. - ; EH is our sector index, L is our offset within the sector. - - ld d, e ; cylinder - ld a, h ; sector - ; Let's process D first. Because our maximum number of sectors is 400 - ; (40 * 10), D can only be either 0 or 1. If it's 1, we set D to 25 and - ; add 6 to A - inc d \ dec d - jr z, .loop1 ; skip - ld d, 25 - add a, 6 -.loop1: - cp FLOPPY_SEC_PER_CYL - jr c, .loop1end - sub FLOPPY_SEC_PER_CYL - inc d - jr .loop1 -.loop1end: - ld e, a ; write final sector in E - ; Let's first see if our first buffer has our sector - ld a, (FLOPPY_BUFSEC1) ; sector - cp e - jr nz, .notBuf1 - ld a, (FLOPPY_BUFSEC1+1) ; cylinder - cp d - jr z, .buf1Ok - -.notBuf1: - ; Ok, let's check for buf2 then - ld a, (FLOPPY_BUFSEC2) ; sector - cp e - jr nz, .notBuf2 - ld a, (FLOPPY_BUFSEC2+1) ; cylinder - cp d - jr z, .buf2Ok - -.notBuf2: - ; None of our two buffers have the sector we need, we'll need to load - ; a new one. - - ; We select our buffer depending on which is dirty. If both are on the - ; same status of dirtiness, we pick any (the first in our case). If one - ; of them is dirty, we pick the clean one. - push de ; --> lvl 1 - ld de, FLOPPY_BUFSEC1 - ld a, (FLOPPY_BUFDIRTY1) - or a ; is buf1 dirty? - jr z, .ready ; no? good, that's our buffer - ; yes? then buf2 is our buffer. - ld de, FLOPPY_BUFSEC2 - -.ready: - ; At this point, DE points to one of our two buffers, the good one. - ; Let's save it to FLOPPY_BUFPTR - ld (FLOPPY_BUFPTR), de - - pop de ; <-- lvl 1 - - ; We have to read a new sector, but first, let's write the current one - ; if needed. - call floppyWrSec - jr nz, .end ; error - ; Let's read our new sector in DE - call floppyRdSec - jr .end - -.buf1Ok: - ld de, FLOPPY_BUFSEC1 - ld (FLOPPY_BUFPTR), de - ; Z already set - jr .end - -.buf2Ok: - ld de, FLOPPY_BUFSEC2 - ld (FLOPPY_BUFPTR), de - ; Z already set - ; to .end -.end: - pop de - ret - -; Flush floppy buffers if dirty and then invalidates them. -; We invalidate them so that we allow the case where we swap disks after a -; flush. If we didn't invalidate the buffers, reading a swapped disk after a -; flush would yield data from the previous disk. -floppyFlush: - ld hl, FLOPPY_BUFSEC1 - ld (FLOPPY_BUFPTR), hl - call floppyWrSec - ld hl, FLOPPY_BUFSEC2 - ld (FLOPPY_BUFPTR), hl - call floppyWrSec - call floppyInit - xor a ; ensure Z - ret - -; *** blkdev routines *** - -; Make HL point to its proper place in FLOPPY_BUF. -; EHL currently is a 24-bit offset to read in the floppy. E=high byte, -; HL=low word. Load the proper sector in memory and make HL point to the -; correct data in the memory buffer. -_floppyPlaceBuf: - call floppySync - ret nz ; error - ; At this point, we have the proper buffer in place and synced in - ; (FLOPPY_BUFPTR). Only L is important - ld a, l - ld hl, (FLOPPY_BUFPTR) - inc hl ; sector MSB - inc hl ; dirty flag - inc hl ; contents - ; DE is now placed on the data part of the active buffer and all we need - ; is to increase DE by L. - call addHL - ; Now, HL points exactly at the right byte in the active buffer. - xor a ; ensure Z - ret - -floppyGetB: - push hl - call _floppyPlaceBuf - jr nz, .end ; NZ already set - - ; This is it! - ld a, (hl) - cp a ; ensure Z -.end: - pop hl - ret - -floppyPutB: - push hl - push af ; --> lvl 1. let's remember the char we put, - ; _floppyPlaceBuf destroys A. - call _floppyPlaceBuf - jr nz, .error - - ; HL points to our dest. Recall A and write - pop af ; <-- lvl 1 - ld (hl), a - - ; Now, let's set the dirty flag - ld a, 1 - ld hl, (FLOPPY_BUFPTR) - inc hl ; sector MSB - inc hl ; point to dirty flag - ld (hl), a ; set dirty flag - xor a ; ensure Z - jr .end -.error: - ; preserve error code - ex af, af' - pop af ; <-- lvl 1 - ex af, af' - call unsetZ -.end: - pop hl - ret diff --git a/kernel/trs80/kbd.asm b/kernel/trs80/kbd.asm deleted file mode 100644 index 49cc088..0000000 --- a/kernel/trs80/kbd.asm +++ /dev/null @@ -1,10 +0,0 @@ -; kbd - TRS-80 keyboard -; -; Implement GetC for TRS-80's keyboard using the system's SVCs. - -trs80GetC: - push de ; altered by SVC - ld a, 0x01 ; @KEY - rst 0x28 ; --> A - pop de - ret diff --git a/kernel/trs80/vid.asm b/kernel/trs80/vid.asm deleted file mode 100644 index b58b04f..0000000 --- a/kernel/trs80/vid.asm +++ /dev/null @@ -1,59 +0,0 @@ -; vid - TRS-80's video -; -; Implement PutC and GRID_SETCELL using TRS-80's SVC calls. - -.equ TRS80_COLS 80 -.equ TRS80_ROWS 24 - -trs80PutC: - push af - push bc - push de ; altered by SVC - ld c, a - ld a, 0x02 ; @DSP - rst 0x28 - pop de - pop bc - pop af - ret - -trs80SetCell: - push af - push bc - push hl ; HL altered by @VDCTL - push de ; DE altered by @VDCTL - ex de, hl - bit 0, c - ld c, a ; save A now - jr z, .skip ; Z from BIT above. cursor not set - ; set cursor - ld a, 0x0f ; @VDCTL - ld b, 3 ; move cursor fn - rst 0x28 - ; HL altered. - ; Our Row/Col is our currently-pushed DE value. Let's take advantage of - ; that. - pop hl \ push hl ; HL altered. bring back from stack -.skip: - ld a, 0x0f ; @VDCTL - ld b, 2 ; display char - rst 0x28 - pop de - pop hl - pop bc - pop af - ret - -; This is a much faster version of gridPushScr. Use it in your glue code, but -; you need to set HL to GRID_BUF first. -trs80PushScr: - push af - push bc - ld a, 0x0f ; @VDCTL - ld b, 5 ; move from RAM to vid - ; HL is already set by caller - rst 0x28 - pop bc - pop af - cp a ; ensure Z - ret diff --git a/kernel/user.h.example b/kernel/user.h.example deleted file mode 100644 index c331bd1..0000000 --- a/kernel/user.h.example +++ /dev/null @@ -1,7 +0,0 @@ -; Example include file for userspace program -; All userspace programs include this file in they main source file so that -; they know a couple of vital things about the system such as where user memory -; starts. This is an example file with required constants. - -.equ USER_CODE 0x4800 ; Where in memory user code is loaded -.equ USER_RAMSTART 0x8800 ; Where in memory user memory begins diff --git a/tests/avra/blink_tn45.asm b/tests/avra/blink_tn45.asm deleted file mode 100644 index ef63bef..0000000 --- a/tests/avra/blink_tn45.asm +++ /dev/null @@ -1,42 +0,0 @@ -; REGISTER USAGE -; -; R1: overflow counter -; R16: tmp stuff - -.inc "avr.h" -.inc "tn254585.h" -.inc "tn45.h" - -main: - ldi r16, RAMEND&0xff - out SPL, r16 - ldi r16, RAMEND}8 - out SPH, r16 - - sbi DDRB, 0 - cbi PORTB, 0 - - ; To have a blinking delay that's visible, we have to prescale a lot. - ; The maximum prescaler is 1024, which makes our TCNT0 increase - ; 976 times per second, which means that it overflows 4 times per - ; second. - in r16, TCCR0B - ori r16, 0x05 ; CS00 + CS02 = 1024 prescaler - out TCCR0B, r16 - - clr r1 - -loop: - in r16, TIFR ; TIFR0 - sbrc r16, 1 ; is TOV0 flag clear? - rcall toggle - rjmp loop - -toggle: - ldi r16, 0b00000010 ; TOV0 - out TIFR, R16 - inc r1 - cbi PORTB, 0 - sbrs r1, 1 ; if LED is on - sbi PORTB, 0 - ret diff --git a/tests/avra/blink_tn45.expected b/tests/avra/blink_tn45.expected deleted file mode 100644 index b5f1e9e..0000000 --- a/tests/avra/blink_tn45.expected +++ /dev/null @@ -1 +0,0 @@ -å ŋāŋļšĀ˜·`ŋ$·ýÐüÏāŋ”Ā˜þĀš• \ No newline at end of file diff --git a/tests/avra/runtests.sh b/tests/avra/runtests.sh deleted file mode 100755 index e1464dc..0000000 --- a/tests/avra/runtests.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -e - -ZASM=../../emul/zasm/avra -AVRINC=../../avr - -cmpas() { - FN=$1 - EXPECTED=$(xxd ${FN%.*}.expected) - ACTUAL=$(cat ${FN} | "${ZASM}" "${AVRINC}" | xxd) - if [ "$ACTUAL" = "$EXPECTED" ]; then - echo ok - else - echo actual - echo "$ACTUAL" - echo expected - echo "$EXPECTED" - exit 1 - fi -} - -if [ ! -z $1 ]; then - cmpas $1 - exit 0 -fi - -for fn in *.asm; do - echo "Comparing ${fn}" - cmpas $fn -done diff --git a/tests/avra/seg7multiplex.asm b/tests/avra/seg7multiplex.asm deleted file mode 100644 index 7075ff3..0000000 --- a/tests/avra/seg7multiplex.asm +++ /dev/null @@ -1,343 +0,0 @@ -; This is a copy of my seg7multiplex main program, translated for zasm. -; The output of zasm was verified against avra's. - -; 7-segments multiplexer for an ATtiny45 -; -; Register usage -; R0: Digit on AFF1 (rightmost, QH on the SR) -; R1: Digit on AFF2 (QG on the SR) -; R2: Digit on AFF3 (QF on the SR) -; R3: Digit on AFF4 (leftmost, QE on the SR) -; R5: always zero -; R6: generic tmp value -; R16: generic tmp value -; R18: value to send to the SR. cleared at every SENDSR call -; in input mode, holds the input buffer -; R30: (low Z) current digit being refreshed. cycles from 0 to 3 -; -; Flags on GPIOs -; GPIOR0 - bit 0: Whether we need to refresh the display -; GPIOR0 - bit 1: Set when INT_INT0 has received a new bit -; GPIOR0 - bit 2: The value of the new bit received -; GPIOR0 - bit 4: input mode enabled - -; Notes on register usage -; R0 - R3: 4 low bits are for digit, 5th bit is for dot. other bits are unused. -; -; Notes on AFF1-4 -; They are reversed (depending on how you see things...). They read right to -; left. That means that AFF1 is least significant, AFF4 is most. -; -; Input mode counter -; When in input mode, TIMER0_OVF, instead of setting the refresh flag, increases -; the counter. When it reaches 3, we timeout and consider input invalid. -; -; Input procedure -; -; Input starts at INT_INT0. What it does there is very simple: is sets up a flag -; telling it received something and conditionally sets another flag with the -; value of the received bit. -; -; While we do that, we have the input loop eagerly checking for that flag. When -; it triggers, it records the bit in R18. The way it does so is that it inits -; R18 at 1 (not 0), then for every bit, it left shifts R18, then adds the new -; bit. When the 6th bit of R18 is set, it means we have every bit we need, we -; can flush it into Z. - -; Z points directly to R3, then R2, then R1, then R0. Because display refresh -; is disabled during input, it won't result in weird displays, and because -; partial numbers result in error display, then partial result won't lead to -; weird displays, just error displays. -; -; When input mode begins, we change Z to point to R3 (the first digit we -; receive) and we decrease the Z pointer after every digit we receive. When we -; receive the last bit of the last digit and that we see that R30 is 0, we know -; that the next (and last) digit is the checksum. - -.inc "avr.h" -.inc "tn254585.h" -.inc "tn45.h" - -; pins -.equ RCLK 0 ; on PORTB -.equ SRCLK 3 ; on PORTB -.equ SER_DP 4 ; on PORTB -.equ INSER 1 ; on PORTB - -; Let's begin! - -.org 0x0000 - RJMP MAIN - RJMP INT_INT0 - RETI ; PCINT0 - RETI ; TIMER1_COMPA - RETI ; TIMER1_OVF - RJMP INT_TIMER0_OVF - -MAIN: - LDI R16, RAMEND&0xff - OUT SPL, R16 - LDI R16, RAMEND}8 - OUT SPH, R16 - - SBI DDRB, RCLK - SBI DDRB, SRCLK - SBI DDRB, SER_DP - - ; we generally keep SER_DP high to avoid lighting DP - SBI PORTB, SER_DP - - ; target delay: 600us. At 1Mhz, that's 75 ticks with a 1/8 prescaler. - LDI R16, 0x02 ; CS01, 1/8 prescaler - OUT TCCR0B, R16 - LDI R16, 0xb5 ; TOP - 75 ticks - OUT TCNT0, R16 - - ; Enable TIMER0_OVF - IN R16, TIMSK - ORI R16, 0x02 ; TOIE0 - OUT TIMSK, R16 - - ; Generate interrupt on rising edge of INT0 - IN R16, MCUCR - ORI R16, 0b00000011 ; ISC00 + ISC01 - OUT MCUCR, R16 - IN R16, GIMSK - ORI R16, 0b01000000 ; INT0 - OUT GIMSK, R16 - - ; we never use indirect addresses above 0xff through Z and never use - ; R31 in other situations. We can set it once and forget about it. - CLR R31 ; high Z - - ; put 4321 in R2-5 - CLR R30 ; low Z - LDI R16, 0x04 - ST Z+, R16 ; 4 - DEC R16 - ST Z+, R16 ; 3 - DEC R16 - ST Z+, R16 ; 2 - DEC R16 - ORI R16, 0b00010000 ; DP - ST Z, R16 ; 1 - CLR R30 ; replace Z to 0 - - SEI - -LOOP: - RCALL INPT_CHK ; verify that we shouldn't enter input mode - SBIC GPIOR0, 0 ; refesh flag cleared? skip next - RCALL RDISP - RJMP LOOP - -; ***** DISPLAY ***** - -; refresh display with current number -RDISP: - ; First things first: setup the timer for the next time - LDI R16, 0xb5 ; TOP - 75 ticks - OUT TCNT0, R16 - CBI GPIOR0, 0 ; Also, clear the refresh flag - - ; Let's begin with the display selector. We select one display at once - ; (not ready for multi-display refresh operations yet). Let's decode our - ; binary value from R30 into R16. - MOV R6, R30 - INC R6 ; we need values 1-4, not 0-3 - LDI R16, 0x01 -RDISP1: - DEC R6 - BREQ RDISP2 ; == 0? we're finished - LSL R16 - RJMP RDISP1 - - ; select a digit to display - ; we do so in a clever way: our registers just happen to be in SRAM - ; locations 0x00, 0x01, 0x02 and 0x03. Handy eh! -RDISP2: - LD R18, Z+ ; Indirect load of Z into R18 then increment - CPI R30, 4 - BRCS RDISP3 ; lower than 4 ? don't reset - CLR R30 ; not lower than 4? reset - - ; in the next step, we're going to join R18 and R16 together, but - ; before we do, we have one thing to process: R18's 5th bit. If it's - ; high, it means that DP is highlighted. We have to store this - ; information in R6 and use it later. Also, we have to clear the higher - ; bits of R18. -RDISP3: - SBRC R18, 4 ; 5th bit cleared? skip next - INC R6 ; if set, then set R6 as well - ANDI R18, 0xf ; clear higher bits - - ; Now we have our display selector in R16 and our digit to display in - ; R18. We want it all in R18. - SWAP R18 ; digit goes in high "nibble" - OR R18, R16 - - ; While we send value to the shift register, SER_DP will change. - ; Because we want to avoid falsely lighting DP, we need to disable - ; output (disable OE) while that happens. This is why we set RCLK, - ; which is wired to OE too, HIGH (OE disabled) at the beginning of - ; the SR operation. - ; - ; Because RCLK was low before, this triggers a "buffer clock" on - ; the SR, but it doesn't matter because the value that was there - ; before has just been invalidated. - SBI PORTB, RCLK ; high - RCALL SENDSR - ; Flush out the buffer with RCLK - CBI PORTB, RCLK ; OE enabled, but SR buffer isn't flushed - NOP - SBI PORTB, RCLK ; SR buffer flushed, OE disabled - NOP - CBI PORTB, RCLK ; OE enabled - - ; We're finished! Oh no wait, one last thing: should we highlight DP? - ; If we should, then we should keep SER_DP low rather than high for this - ; SR round. - SBI PORTB, SER_DP ; SER_DP generally kept high - SBRC R6, 0 ; R6 is cleared? skip DP set - CBI PORTB, SER_DP ; SER_DP low highlight DP - - RET ; finished for real this time! - -; send R18 to shift register. -; We send highest bits first so that QH is the MSB and QA is the LSB -; low bits (QD - QA) control display's power -; high bits (QH - QE) select the glyph -SENDSR: - LDI R16, 8 ; we will loop 8 times - CBI PORTB, SER_DP ; low - SBRC R18, 7 ; if latest bit isn't cleared, set SER_DP high - SBI PORTB, SER_DP ; high - RCALL TOGCP - LSL R18 ; shift our data left - DEC R16 - BRNE SENDSR+2 ; not zero yet? loop! (+2 to avoid reset) - RET - -; toggle SRCLK, waiting 1us between pin changes -TOGCP: - CBI PORTB, SRCLK ; low - NOP ; At 1Mhz, this is enough for 1us - SBI PORTB, SRCLK ; high - RET - -; ***** INPUT MODE ***** - -; check whether we should enter input mode and enter it if needed -INPT_CHK: - SBIS GPIOR0, 1 ; did we just trigger INT_INT0? - RET ; no? return - ; yes? continue in input mode - -; Initialize input mode and start the loop -INPT_BEGIN: - SBI GPIOR0, 4 ; enable input mode - CBI GPIOR0, 1 ; The first trigger was an empty one - - ; At 1/8 prescaler, a "full" counter overflow is 2048us. That sounds - ; about right for an input timeout. So we co the easy route and simply - ; clear TCNT0 whenever we want to reset the timer - OUT TCNT0, R5 ; R5 == 0 - CBI GPIOR0, 0 ; clear refresh flag in case it was just set - LDI R30, 0x04 ; make Z point on R3+1 (we use pre-decrement) - LDI R18, 0x01 ; initialize input buffer - -; loop in input mode. When in input mode, we don't refresh the display, we use -; all our processing power to process input. -INPT_LOOP: - RCALL INPT_READ - - ; Check whether we've reached timeout - SBIC GPIOR0, 0 ; refesh flag cleared? skip next - RCALL INPT_TIMEOUT - - SBIC GPIOR0, 4 ; input mode cleared? skip next, to INPT_END - RJMP INPT_LOOP ; not cleared? loop - -INPT_END: - ; We received all our date or reached timeout. let's go back in normal - ; mode. - CLR R30 ; Ensure Z isn't out of bounds - SBI GPIOR0, 0 ; set refresh flag so we start refreshing now - RET - -; Read, if needed, the last received bit -INPT_READ: - SBIS GPIOR0, 1 - RET ; flag cleared? nothing to do - - ; Flag is set, we have to read - CBI GPIOR0, 1 ; unset flag - LSL R18 - SBIC GPIOR0, 2 ; data flag cleared? skip next - INC R18 - - ; Now, let's check if we have our 5 digits - SBRC R18, 5 ; 6th bit cleared? nothing to do - RCALL INPT_PUSH - - OUT TCNT0, R5 ; clear timeout counter - - RET - -; Push the digit currently in R18 in Z and reset R18. -INPT_PUSH: - ANDI R18, 0b00011111 ; Remove 6th bit flag - - TST R30 ; is R30 zero? - BREQ INPT_CHECKSUM ; yes? it means we're at checksum phase. - - ; Otherwise, its a regular digit push - ST -Z, R18 - LDI R18, 0x01 - RET - -INPT_CHECKSUM: - CBI GPIOR0, 4 ; clear input mode, whether we error or not - MOV R16, R0 - ADD R16, R1 - ADD R16, R2 - ADD R16, R3 - ; only consider the first 5 bits of the checksum since we can't receive - ; more. Otherwise, we couldn't possibly validate a value like 9999 - ANDI R16, 0b00011111 - CP R16, R18 - BRNE INPT_ERROR - RET - -INPT_TIMEOUT: - CBI GPIOR0, 4 ; timeout reached, clear input flag - ; continue to INPT_ERROR - -INPT_ERROR: - LDI R16, 0x0c ; some weird digit - MOV R0, R16 - MOV R1, R16 - MOV R2, R16 - MOV R3, R16 - RET - -; ***** INTERRUPTS ***** - -; Record received bit -; The main loop has to be fast enough to process that bit before we receive the -; next one! -; no SREG fiddling because no SREG-modifying instruction -INT_INT0: - CBI GPIOR0, 2 ; clear received data - SBIC PINB, INSER ; INSER clear? skip next - SBI GPIOR0, 2 ; INSER set? record this - SBI GPIOR0, 1 ; indicate that we've received a bit - RETI - -; Set refresh flag whenever timer0 overflows -; no SREG fiddling because no SREG-modifying instruction -INT_TIMER0_OVF: - SBI GPIOR0, 0 - RETI - - diff --git a/tests/avra/seg7multiplex.expected b/tests/avra/seg7multiplex.expected deleted file mode 100644 index 1cb0a70..0000000 Binary files a/tests/avra/seg7multiplex.expected and /dev/null differ diff --git a/tests/avra/test1.asm b/tests/avra/test1.asm deleted file mode 100644 index 7bbcd24..0000000 --- a/tests/avra/test1.asm +++ /dev/null @@ -1,28 +0,0 @@ -add r1, r31 -ret -foo: -sleep -break -breq bar -asr r20 -bar: -brbs 6, foo -ori r22, 0x34+4 -sbrs r1, 3 -rjmp foo -rcall baz -baz: -out 0x2e, r12 -in r0, 0x9 -cbr r31, 0xff -sbis 22, 5 -ser r19 -bset 4 -bclr 7 -call foo -jmp bar -mov r6, r30 -lsl r3 -tst r12 -clr YH -clr r29 diff --git a/tests/avra/test1.expected b/tests/avra/test1.expected deleted file mode 100644 index ae925a4..0000000 Binary files a/tests/avra/test1.expected and /dev/null differ diff --git a/tests/avra/testldst.asm b/tests/avra/testldst.asm deleted file mode 100644 index e21bfb0..0000000 --- a/tests/avra/testldst.asm +++ /dev/null @@ -1,18 +0,0 @@ -ld r0, X -ld r1, Y -ld r2, Z -ld r3, X+ -ld r4, Y+ -ld r5, Z+ -ld r6, -X -ld r7, -Y -ld r8, -Z -st X, r9 -st Y, r10 -st Z, r11 -st X+, r12 -st Y+, r13 -st Z+, r14 -st -X, r15 -st -Y, r16 -st -Z, r17 diff --git a/tests/avra/testldst.expected b/tests/avra/testldst.expected deleted file mode 100644 index e8e55e1..0000000 --- a/tests/avra/testldst.expected +++ /dev/null @@ -1,2 +0,0 @@ - € €=IQnz‚œ’Ļ‚°‚Í’Ų’á’þ’ -““ \ No newline at end of file diff --git a/tests/shell/cfsin/bar b/tests/shell/cfsin/bar deleted file mode 100644 index 839d375..0000000 --- a/tests/shell/cfsin/bar +++ /dev/null @@ -1 +0,0 @@ -Hello Bar! diff --git a/tests/shell/cfsin/foo b/tests/shell/cfsin/foo deleted file mode 100644 index 953a6f4..0000000 --- a/tests/shell/cfsin/foo +++ /dev/null @@ -1 +0,0 @@ -Hello Foo! diff --git a/tests/shell/fls.expected b/tests/shell/fls.expected deleted file mode 100644 index ce602af..0000000 --- a/tests/shell/fls.expected +++ /dev/null @@ -1,5 +0,0 @@ -Collapse OS -> fls -bar -foo -> diff --git a/tests/shell/fls.replay b/tests/shell/fls.replay deleted file mode 100644 index b9cf197..0000000 --- a/tests/shell/fls.replay +++ /dev/null @@ -1 +0,0 @@ -fls diff --git a/tests/shell/print.expected b/tests/shell/print.expected deleted file mode 100644 index 4fe6e58..0000000 --- a/tests/shell/print.expected +++ /dev/null @@ -1,4 +0,0 @@ -Collapse OS -> print 42 -42 -> diff --git a/tests/shell/print.replay b/tests/shell/print.replay deleted file mode 100644 index 2e97662..0000000 --- a/tests/shell/print.replay +++ /dev/null @@ -1 +0,0 @@ -print 42 diff --git a/tests/shell/runtests.sh b/tests/shell/runtests.sh deleted file mode 100755 index 0b962eb..0000000 --- a/tests/shell/runtests.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -e - -EMULDIR=../../emul -SHELL="${EMULDIR}/shell/shell" - -replay() { - fn=$1 - replayfn=${fn%.*}.expected - ACTUAL=$("${SHELL}" -f test.cfs < "${fn}" 2> /dev/null) - EXPECTED=$(cat ${replayfn}) - if [ "$ACTUAL" = "$EXPECTED" ]; then - echo ok - else - echo different. Whole output: - echo "${ACTUAL}" - exit 1 - fi -} - -if [ ! -z $1 ]; then - replay $1 - exit 0 -fi - -for fn in *.replay; do - echo "Replaying ${fn}" - replay "${fn}" -done diff --git a/tests/shell/test.cfs b/tests/shell/test.cfs deleted file mode 100644 index f9fb1b6..0000000 Binary files a/tests/shell/test.cfs and /dev/null differ diff --git a/tests/unit/common.asm b/tests/unit/common.asm deleted file mode 100644 index 6d48a3d..0000000 --- a/tests/unit/common.asm +++ /dev/null @@ -1,131 +0,0 @@ -; *** requirements *** -; ascii.h -; core -; stdio -; lib/ari -; lib/fmt - -testNum: .db 1 -; Each time we call assertSP, we verify that our stack isn't imbalanced by -; comparing SP to its saved value. Whenever your "base" SP value change, -; generally at the beginning of a test routine, run "ld (testSP), sp" to have -; proper value saved to heap. -testSP: .dw 0xffff - - -STDIO_PUTC: - out (0), a - cp a - ret - -STDIO_GETC: - jp unsetZ - -assertZ: - ret z - ld hl, .msg - call printstr - jp fail -.msg: - .db "Z not set", CR, LF, 0 - -assertNZ: - ret nz - ld hl, .msg - call printstr - jp fail -.msg: - .db "Z set", CR, LF, 0 - -assertC: - ret c - ld hl, .msg - call printstr - jp fail -.msg: - .db "C not set", CR, LF, 0 - -assertNC: - ret nc - ld hl, .msg - call printstr - jp fail -.msg: - .db "C set", CR, LF, 0 - -; Assert that A == B -assertEQB: - cp b - ret z - call printHex - call printcrlf - ld a, b - call printHex - call printcrlf - ld hl, .msg - call printstr - jp fail -.msg: - .db "A != B", CR, LF, 0 - -; Assert that HL == DE -assertEQW: - ld a, h - cp d - jr nz, .fail - ld a, l - cp e - ret z -.fail: - call printHexPair - call printcrlf - ex de, hl - call printHexPair - call printcrlf - ld hl, .msg - call printstr - jp fail -.msg: - .db "HL != DE", CR, LF, 0 - -; Given a list of pointer to test data structures in HL and a pointer to a test -; routine in IX, call (IX) with HL pointing to the test structure until the list -; points to a zero. See testParseExpr in test_expr for an example usage. -testList: - push hl ; --> lvl 1 - call intoHL - ld a, h - or l - jr z, .end - call callIX - call nexttest - pop hl ; <-- lvl 1 - inc hl \ inc hl - jr testList -.end: - pop hl ; <-- lvl 1 - ret - -; test that SP == testSP -assertSP: - ld hl, (testSP) - ; offset the fact that we call assertSP - dec hl \ dec hl - or a ; reset carry - sbc hl, sp - ret z - ld hl, .msg - call printstr - jr fail -.msg: - .db "Wrong SP", CR, LF, 0 - -nexttest: - ld a, (testNum) - inc a - ld (testNum), a - ret - -fail: - ld a, (testNum) - halt diff --git a/tests/unit/runtests.sh b/tests/unit/runtests.sh deleted file mode 100755 index 09318b9..0000000 --- a/tests/unit/runtests.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -e -# TODO: find POSIX substitute to that PIPESTATUS thing - -BASE=../.. -ZASM="${BASE}/emul/zasm/zasm" -RUNBIN="${BASE}/emul/runbin/runbin" -KERNEL="${BASE}/kernel" -APPS="${BASE}/apps" - -chk() { - echo "Running test $1" - if ! ${ZASM} "${KERNEL}" "${APPS}" common.asm < $1 | ${RUNBIN}; then - echo "failed with code ${PIPESTATUS[1]}" - exit 1 - fi -} - -if [ ! -z $1 ]; then - chk $1 - exit 0 -fi - -for fn in test_*.asm; do - chk "${fn}" -done - -echo "All tests passed!" diff --git a/tests/unit/test_basic_parse.asm b/tests/unit/test_basic_parse.asm deleted file mode 100644 index 3c667d1..0000000 --- a/tests/unit/test_basic_parse.asm +++ /dev/null @@ -1,95 +0,0 @@ -jp test - -.inc "ascii.h" -.inc "core.asm" -.inc "str.asm" -.inc "lib/util.asm" -.inc "lib/ari.asm" -.inc "lib/parse.asm" -.equ EXPR_PARSE parseLiteral -.inc "lib/expr.asm" -.inc "basic/parse.asm" -.inc "lib/fmt.asm" -.inc "stdio.asm" -.inc "common.asm" - -test: - ld sp, 0xffff - - call testParseThruth - - ; success - xor a - halt - -testParseThruth: - ld hl, .t1 - call .true - ld hl, .t2 - call .true - ld hl, .t3 - call .true - ld hl, .t4 - call .true - ld hl, .t5 - call .true - ld hl, .t6 - call .true - ld hl, .t7 - call .true - ld hl, .t8 - call .true - - ld hl, .f1 - call .false - ld hl, .f2 - call .false - ld hl, .f3 - call .false - ld hl, .f4 - call .false - ld hl, .f5 - call .false - ld hl, .f6 - call .false - - ld hl, .e1 - call .error - ret - -.true: - call parseTruth - call assertZ - or a - call assertNZ - jp nexttest - -.false: - call parseTruth - call assertZ - or a - call assertZ - jp nexttest - -.error: - call parseTruth - call assertNZ - jp nexttest - -.t1: .db "42", 0 -.t2: .db "42+4=50-4", 0 -.t3: .db "1<2", 0 -.t4: .db "2>1", 0 -.t5: .db "2>=1", 0 -.t6: .db "2>=2", 0 -.t7: .db "1<=2", 0 -.t8: .db "2<=2", 0 -.f1: .db "42-42", 0 -.f2: .db "42+4=33+2", 0 -.f3: .db "2<2", 0 -.f4: .db "1>2", 0 -.f5: .db "1>=2", 0 -.f6: .db "2<=1", 0 -.e1: .db "foo", 0 - -STDIO_RAMSTART: diff --git a/tests/unit/test_core.asm b/tests/unit/test_core.asm deleted file mode 100644 index 1ae3b06..0000000 --- a/tests/unit/test_core.asm +++ /dev/null @@ -1,67 +0,0 @@ -.equ foo 456 ; AFTER_ORG should not get that value -.org 0x1234 -.equ AFTER_ORG @ -.org 0 - -jp test - -.inc "ascii.h" -.inc "core.asm" -.inc "lib/ari.asm" -.inc "lib/fmt.asm" -.inc "stdio.asm" -.inc "common.asm" - -dummyLabel: - -.equ dummyLabel 0x42 - -test: - ld hl, 0xffff - ld sp, hl - - ; *** Just little z80 flags memo. - and a ; clear carry - ld hl, 100 - ld de, 101 - sbc hl, de - jp nc, fail ; carry is set - call nexttest - - and a ; clear carry - ld hl, 101 - ld de, 100 - sbc hl, de - jp c, fail ; carry is reset - call nexttest - - ld a, 1 - dec a - jp m, fail ; positive - dec a - jp p, fail ; negative - call nexttest - - ; Test that .equ can override label - ld de, 0x42 - ld hl, dummyLabel - call assertEQW - call nexttest - - ; test that "@" is updated by a .org directive - ld hl, AFTER_ORG - ld de, 0x1234 - call assertEQW - call nexttest - - ; test that AND affects the Z flag - ld a, 0x69 - and 0x80 - call assertZ - call nexttest - - ; success - xor a - halt - -STDIO_RAMSTART: diff --git a/tests/unit/test_ed_util.asm b/tests/unit/test_ed_util.asm deleted file mode 100644 index f7af5af..0000000 --- a/tests/unit/test_ed_util.asm +++ /dev/null @@ -1,42 +0,0 @@ -jp test - -.inc "ed/util.asm" - -test: - ld sp, 0xffff - - ; *** cpHLDE *** - ld hl, 0x42 - ld de, 0x42 - call cpHLDE - jp nz, fail - jp c, fail - call nexttest - - ld de, 0x4242 - call cpHLDE - jp z, fail - jp nc, fail - call nexttest - - ld hl, 0x4243 - call cpHLDE - jp z, fail - jp c, fail - call nexttest - - ; success - xor a - halt - -testNum: .db 1 - -nexttest: - ld a, (testNum) - inc a - ld (testNum), a - ret - -fail: - ld a, (testNum) - halt diff --git a/tests/unit/test_expr.asm b/tests/unit/test_expr.asm deleted file mode 100644 index 8ef5a6a..0000000 --- a/tests/unit/test_expr.asm +++ /dev/null @@ -1,155 +0,0 @@ -.equ RAMSTART 0x4000 -.equ ZASM_REG_MAXCNT 0xff -.equ ZASM_LREG_MAXCNT 0x40 -.equ ZASM_REG_BUFSZ 0x1000 -.equ ZASM_LREG_BUFSZ 0x200 - -; declare DIREC_LASTVAL manually so that we don't have to include directive.asm -.equ DIREC_LASTVAL RAMSTART - -jp test - -.inc "ascii.h" -.inc "core.asm" -.inc "str.asm" -.inc "lib/util.asm" -.inc "lib/ari.asm" -.inc "lib/fmt.asm" -.inc "zasm/util.asm" -.inc "zasm/const.asm" -.inc "lib/parse.asm" -.inc "zasm/parse.asm" -.equ SYM_RAMSTART DIREC_LASTVAL+2 -.inc "zasm/symbol.asm" -.equ EXPR_PARSE parseNumberOrSymbol -.inc "lib/expr.asm" -.equ STDIO_RAMSTART SYM_RAMEND -.inc "stdio.asm" -.inc "common.asm" - -; Pretend that we aren't in first pass -zasmIsFirstPass: - jp unsetZ - -zasmGetPC: - ret - - -sFOO: .db "FOO", 0 -sBAR: .db "BAR", 0 - -test: - ld sp, 0xffff - - ; before testing begins, let's set up FOO and BAR symbols - call symInit - ld hl, sFOO - ld de, 0x4000 - call symRegisterGlobal - jp nz, fail - ld hl, sBAR - ld de, 0x20 - call symRegisterGlobal - jp nz, fail - - call testParseExpr - call testSPOnFail - - ; success - xor a - halt - -testParseExpr: - ld hl, .alltests - ld ix, .test - jp testList - -.test: - push hl \ pop iy - inc hl \ inc hl - call parseExpr - call assertZ - ld l, (iy) - ld h, (iy+1) - jp assertEQW - -.t1: - .dw 7 - .db "42/6", 0 -.t2: - .dw 1 - .db "7%3", 0 -.t3: - .dw 0x0907 - .db "0x99f7&0x0f0f", 0 -.t4: - .dw 0x9fff - .db "0x99f7|0x0f0f", 0 -.t5: - .dw 0x96f8 - .db "0x99f7^0x0f0f", 0 -.t6: - .dw 0x133e - .db "0x99f7}3", 0 -.t7: - .dw 0xcfb8 - .db "0x99f7{3", 0 -.t8: - .dw 0xffff - .db "-1", 0 -.t9: - .dw 10 - .db "2*3+4", 0 - -; There was this untested regression during the replacement of find-and-subst -; parseExpr to the recursive descent one. It was time consuming to find. Here -; it goes, here it stays. -.t10: - .dw '-'+1 - .db "'-'+1", 0 - -.t11: - .dw 0x4023 - .db "0x4001+0x22", 0 - -.t12: - .dw 0x4020 - .db "FOO+BAR", 0 - -.t13: - .dw 0x60 - .db "BAR*3", 0 - -.t14: - .dw 0x3ffd - .db "FOO-3", 0 - -.t15: - .dw 0x4080 - .db "FOO+BAR*4", 0 - -; "0" is a special case, let's test it -.t16: - .dw 0 - .db "0", 0 - -; Another one that caused troubles -.t17: - .dw 123 - .db "0+123", 0 - -.alltests: - .dw .t1, .t2, .t3, .t4, .t5, .t6, .t7, .t8, .t9, .t10, .t11, .t12 - .dw .t13, .t14, .t15, .t16, .t17, 0 - -; Ensure that stack is balanced on failure -testSPOnFail: - ld (testSP), sp - ld hl, .sFail - call parseExpr - call assertNZ - call assertSP - jp nexttest - -.sFail: .db "1+abc123", 0 - diff --git a/tests/unit/test_lib_ari.asm b/tests/unit/test_lib_ari.asm deleted file mode 100644 index b81ba28..0000000 --- a/tests/unit/test_lib_ari.asm +++ /dev/null @@ -1,35 +0,0 @@ -jp test - -.inc "core.asm" -.inc "lib/ari.asm" - -testNum: .db 1 - -test: - ld sp, 0xffff - - ld de, 12 - ld bc, 4 - call multDEBC - ld a, l - cp 48 - jp nz, fail - call nexttest - - ; success - xor a - halt - -nexttest: - ld a, (testNum) - inc a - ld (testNum), a - ret - -fail: - ld a, (testNum) - halt - - - - diff --git a/tests/unit/test_lib_fmt.asm b/tests/unit/test_lib_fmt.asm deleted file mode 100644 index 40e5079..0000000 --- a/tests/unit/test_lib_fmt.asm +++ /dev/null @@ -1,98 +0,0 @@ -jp test - -.inc "core.asm" -.inc "lib/util.asm" -.inc "lib/ari.asm" -.inc "lib/fmt.asm" - -stdioPutC: - ret - -test: - ld sp, 0xffff - - call testFmtDecimal - call testFmtDecimalS - - ; success - xor a - halt - -testFmtDecimal: - ld ix, .t1 - call .test - ld ix, .t2 - call .test - ld ix, .t3 - call .test - ld ix, .t4 - call .test - ld ix, .t5 - call .test - ret -.test: - ld e, (ix) - ld d, (ix+1) - ld hl, sandbox - call fmtDecimal - ld hl, sandbox - push ix \ pop de - inc de \ inc de - call strcmp - jp nz, fail - jp nexttest -.t1: - .dw 1234 - .db "1234", 0 -.t2: - .dw 9999 - .db "9999", 0 -.t3: - .dw 0 - .db "0", 0 -.t4: - .dw 0x7fff - .db "32767", 0 -.t5: - .dw 0xffff - .db "65535", 0 - -testFmtDecimalS: - ld ix, .t1 - call .test - ld ix, .t2 - call .test - ret -.test: - ld e, (ix) - ld d, (ix+1) - ld hl, sandbox - call fmtDecimalS - ld hl, sandbox - push ix \ pop de - inc de \ inc de - call strcmp - jp nz, fail - jp nexttest -.t1: - .dw 1234 - .db "1234", 0 -.t2: - .dw 0-1234 - .db "-1234", 0 - -testNum: .db 1 - -nexttest: - ld a, (testNum) - inc a - ld (testNum), a - ret - -fail: - ld a, (testNum) - halt - -; used as RAM -sandbox: - diff --git a/tests/unit/test_lib_parse.asm b/tests/unit/test_lib_parse.asm deleted file mode 100644 index 6cd4991..0000000 --- a/tests/unit/test_lib_parse.asm +++ /dev/null @@ -1,195 +0,0 @@ -jp test - -.inc "ascii.h" -.inc "core.asm" -.equ STDIO_RAMSTART RAMSTART -.inc "stdio.asm" -.inc "common.asm" -.inc "lib/ari.asm" -.inc "lib/util.asm" -.inc "lib/fmt.asm" -.inc "lib/parse.asm" - -test: - ld sp, 0xffff - - call testParseHex - call testParseHexadecimal - call testParseDecimal - call testParseLiteral - - ; success - xor a - halt - -testParseHex: - ld hl, .allGood - ld ix, .testGood - call testList - ld hl, .allBad - ld ix, .testBad - jp testList - -.testGood: - ld a, (hl) - call parseHex - call assertNC - inc hl - ld b, (hl) - jp assertEQB - -.testBad: - ld a, (hl) - call parseHex - jp assertC - -.g1: - .db '8', 8 -.g2: - .db 'e', 0xe - -.allGood: - .dw .g1, .g2, 0 - -.b1: - .db 'x' - -.allBad: - .dw .b1, 0 - -testParseHexadecimal: - ld hl, .allGood - ld ix, .testGood - jp testList - -.testGood: - ld c, (hl) - inc hl - ld b, (hl) - inc hl - call parseHexadecimal - call assertZ - ld l, c - ld h, b - jp assertEQW - -.g1: - .dw 0x99 - .db "99", 0 -.g2: - .dw 0xab - .db "aB", 0 -; The string "Foo" will not cause a failure. We will parse up to "o" and then -; stop. -.g3: - .dw 0xf - .db "Foo", 0 - -.allGood: - .dw .g1, .g2, .g3, 0 - -testParseDecimal: - ld hl, .allGood - ld ix, .testGood - call testList - ld hl, .allBad - ld ix, .testBad - jp testList - -.testGood: - ld c, (hl) - inc hl - ld b, (hl) - inc hl - call parseDecimalC - call assertZ - ld l, c - ld h, b - jp assertEQW - -.testBad: - call parseDecimalC - jp assertNZ - -.g1: - .dw 99 - .db "99", 0 -.g2: - .dw 65535 - .db "65535", 0 -; Space is also accepted as a number "ender" -.g3: - .dw 42 - .db "42 x", 0 -; Tab too -.g4: - .dw 42 - .db "42", 0x09, 'x', 0 -; A simple "0" works too! -.g5: - .dw 0 - .db '0', 0 - -.allGood: - .dw .g1, .g2, .g3, .g4, .g5, 0 - -; null string is invalid -.b1: - .db 0 -; too big, 5 chars -.b2: - .db "65536", 0 -.b3: - .db "99999", 0 -.b4: -; too big, 6 chars with rightmost chars being within bound - .db "111111", 0 - -.allBad: - .dw .b1, .b2, .b3, .b4, 0 - -testParseLiteral: - ld hl, .allGood - ld ix, .testGood - call testList - ld hl, .allBad - ld ix, .testBad - jp testList - -.testGood: - ld c, (hl) - inc hl - ld b, (hl) - inc hl - call parseLiteral - call assertZ - ld l, c - ld h, b - jp assertEQW - -.testBad: - call parseLiteral - jp assertNZ - -.g1: - .dw 99 - .db "99", 0 -.g2: - .dw 0x100 - .db "0x100", 0 -.g3: - .dw 0b0101 - .db "0b0101", 0 -.g4: - .dw 0b01010101 - .db "0b01010101", 0 - -.allGood: - .dw .g1, .g2, .g3, .g4, 0 - -.b1: - .db "Foo", 0 -.allBad: - .dw .b1, 0 - -RAMSTART: diff --git a/tests/unit/test_lib_util.asm b/tests/unit/test_lib_util.asm deleted file mode 100644 index f1d28aa..0000000 --- a/tests/unit/test_lib_util.asm +++ /dev/null @@ -1,55 +0,0 @@ -jp test - -.inc "ascii.h" -.inc "core.asm" -.equ STDIO_RAMSTART RAMSTART -.inc "stdio.asm" -.inc "common.asm" -.inc "lib/ari.asm" -.inc "lib/fmt.asm" -.inc "lib/util.asm" - -test: - ld sp, 0xffff - - call testRdWS - - ; success - xor a - halt - -testRdWS: - ld hl, .allGood - ld ix, .testGood - call testList - ld hl, .allBad - ld ix, .testBad - jp testList - -.testGood: - call rdWS - jp assertZ - -.testBad: - call rdWS - jp assertNZ - -; Strings ending with a non-WS, and thus yielding Z -.g1: - .db " X", 0 -.g2: - .db "X", 0 - -.allGood: - .dw .g1, .g2, 0 - -; Strings ending with a WS, and thus yielding NZ -.b1: - .db 0 -.b2: - .db " ", 0 - -.allBad: - .dw .b1, .b2, 0 - -RAMSTART: diff --git a/tests/unit/test_symbol.asm b/tests/unit/test_symbol.asm deleted file mode 100644 index 6700261..0000000 --- a/tests/unit/test_symbol.asm +++ /dev/null @@ -1,69 +0,0 @@ -.equ RAMSTART 0x4000 -.equ ZASM_REG_MAXCNT 0xff -.equ ZASM_LREG_MAXCNT 0x40 -.equ ZASM_REG_BUFSZ 0x1000 -.equ ZASM_LREG_BUFSZ 0x200 - -jp test - -.inc "core.asm" -.inc "str.asm" -.inc "lib/util.asm" -.inc "zasm/util.asm" -.inc "zasm/const.asm" -.equ SYM_RAMSTART RAMSTART -.inc "zasm/symbol.asm" - -testNum: .db 1 - -sFOO: .db "FOO", 0 -sFOOBAR: .db "FOOBAR", 0 -sOther: .db "Other", 0 - -test: - ld sp, 0xffff - - ; Check that we compare whole strings (a prefix will not match a longer - ; string). - call symInit - ld hl, sFOOBAR - ld de, 42 - call symRegisterGlobal - jp nz, fail - ld hl, sFOO - ld de, 43 - call symRegisterGlobal - jp nz, fail - - ld hl, sFOO - call symFindVal ; don't match FOOBAR - jp nz, fail - ld a, d - or a - jp nz, fail - ld a, e - cp 43 - jp nz, fail - call nexttest - - ld hl, sOther - call symFindVal - jp z, fail - call nexttest - - ; success - xor a - halt - -nexttest: - ld hl, testNum - inc (hl) - ret - -fail: - ld a, (testNum) - halt - - - - diff --git a/tests/unit/test_util_z.asm b/tests/unit/test_util_z.asm deleted file mode 100644 index fd461f0..0000000 --- a/tests/unit/test_util_z.asm +++ /dev/null @@ -1,36 +0,0 @@ -jp test - -.inc "core.asm" -.inc "str.asm" -.inc "lib/util.asm" -.inc "zasm/util.asm" - -testNum: .db 1 -sFoo: .db "foo", 0 - -test: - ld hl, 0xffff - ld sp, hl - - ld hl, sFoo - call strlen - cp 3 - jp nz, fail - call nexttest - - ; success - xor a - halt - -nexttest: - ld a, (testNum) - inc a - ld (testNum), a - ret - -fail: - ld a, (testNum) - halt - - - diff --git a/tests/unit/test_z_instr.asm b/tests/unit/test_z_instr.asm deleted file mode 100644 index 8b74036..0000000 --- a/tests/unit/test_z_instr.asm +++ /dev/null @@ -1,178 +0,0 @@ -jp runTests - -.inc "err.h" -.inc "core.asm" -.inc "str.asm" -.inc "zasm/const.asm" -.inc "lib/ari.asm" -.inc "lib/util.asm" -.inc "zasm/util.asm" -.inc "lib/parse.asm" -.equ EXPR_PARSE parseLiteral -.inc "lib/expr.asm" -.equ INS_RAMSTART RAMSTART -.inc "zasm/instr.asm" - -zasmGetPC: - ret - -zasmIsFirstPass: - jp unsetZ - -readWord: -readComma: -symFindVal: - xor a - ret - -ioPutB: - push hl - ld hl, SPITBOWL - push af - ld a, (SPITCNT) - call addHL - inc a - ld (SPITCNT), a - pop af - ld (hl), a - pop hl - cp a - ret - -runTests: - call testMatchArg - call testSpitUpcode - xor a - halt - -testSpitUpcode: - ld iy, .t1 - call .test - ld iy, .t2 - call .test - ld iy, .t3 - call .test - ld iy, .t4 - call .test - ld iy, .t5 - call .test - ret - -.test: - ; init spitbowl - xor a - ld (SPITCNT), a - ld (SPITBOWL), a - ld (SPITBOWL+1), a - ld (SPITBOWL+2), a - ld (SPITBOWL+3), a - push iy \ pop ix - call intoIX - ld a, (iy+2) - ld (INS_CURARG1), a - ld a, (iy+3) - ld (INS_CURARG1+1), a - ld a, (iy+4) - ld (INS_CURARG1+2), a - ld a, (iy+5) - ld (INS_CURARG2), a - ld a, (iy+6) - ld (INS_CURARG2+1), a - ld a, (iy+7) - ld (INS_CURARG2+2), a - call spitUpcode - jp nz, fail - ld a, (SPITCNT) - cp (iy+8) - jp nz, fail - ld a, (SPITBOWL) - cp (iy+9) - jp nz, fail - ld a, (SPITBOWL+1) - cp (iy+10) - jp nz, fail - ld a, (SPITBOWL+2) - cp (iy+11) - jp nz, fail - ld a, (SPITBOWL+3) - cp (iy+12) - jp nz, fail - jp nexttest - -; Test data is a argspec pointer in instrTBl followed by 2*3 bytes of CURARG -; followed by the expected spit, 1 byte cnt + 4 bytes spits. -.t1: - .dw instrTBl+17*6 ; CCF - .db 0, 0, 0 - .db 0, 0, 0 - .db 1, 0x3f, 0, 0, 0 -.t2: - .dw instrTBl+10*6 ; AND (IX+0x42) - .db 'x', 0x42, 0 - .db 0, 0, 0 - .db 3, 0xdd, 0xa6, 0x42, 0 -.t3: - .dw instrTBl+13*6 ; BIT 4, (IX+3) - .db 'N', 4, 0 - .db 'x', 3, 0 - .db 4, 0xdd, 0xcb, 0x03, 0x66 -.t4: - .dw instrTBl+18*6 ; CP (IX+5) - .db 'x', 5, 0 - .db 0, 0, 0 - .db 3, 0xdd, 0xbe, 0x05, 0 -.t5: - .dw instrTBl+4*6 ; ADD A, (IX+5) - .db 'A', 0, 0 - .db 'x', 5, 0 - .db 3, 0xdd, 0x86, 0x05, 0 - -testMatchArg: - ld iy, .t1 - call .test - ret - -.test: - ld hl, SPITBOWL - ld a, (iy+2) - ld (hl), a - push iy \ pop de - call intoDE - push de \ pop ix - ld a, (ix+1) - call matchArg - jp nz, fail - ld a, (iy+3) - ld (hl), a - ld a, (ix+2) - call matchArg - jp nz, fail - jp nexttest - -; Test data is argspec pointer followed by two bytes: first bytes of our two -; CURARG. -.t1: - .dw instrTBl+4*6 ; ADD A, (IX) - .db 'A', 'x' - -nexttest: - ld a, (testNum) - inc a - ld (testNum), a - ret - -fail: - ld a, (testNum) - halt - -testNum: .db 1 - -SPITCNT: - .db 0 -SPITBOWL: - .db 0, 0, 0, 0 - -DIREC_LASTVAL: - .db 0, 0 - -RAMSTART: diff --git a/tests/zasm/allinstrs.asm b/tests/zasm/allinstrs.asm deleted file mode 100644 index e48a0b2..0000000 --- a/tests/zasm/allinstrs.asm +++ /dev/null @@ -1,1965 +0,0 @@ -ADC A, (HL) -ADC A, B -ADC A, C -ADC A, D -ADC A, E -ADC A, H -ADC A, L -ADC A, A -ADC A, 1 -ADC A, 2 -ADC A, 4 -ADC A, 8 -ADC A, 16 -ADC A, 32 -ADC A, 64 -ADC A, 128 -ADC HL, BC -ADC HL, DE -ADC HL, HL -ADC HL, SP -ADD A, (HL) -ADD A, (IX) -ADD A, (IX+1) -ADD A, (IX-1) -ADD A, (IX+10) -ADD A, (IX-10) -ADD A, (IX+100) -ADD A, (IX-100) -ADD A, (IX+127) -ADD A, (IX-127) -ADD A, (IY) -ADD A, (IY+1) -ADD A, (IY-1) -ADD A, (IY+10) -ADD A, (IY-10) -ADD A, (IY+100) -ADD A, (IY-100) -ADD A, (IY+127) -ADD A, (IY-127) -ADD A, B -ADD A, C -ADD A, D -ADD A, E -ADD A, H -ADD A, L -ADD A, A -ADD A, 1 -ADD A, 2 -ADD A, 4 -ADD A, 8 -ADD A, 16 -ADD A, 32 -ADD A, 64 -ADD A, 128 -ADD HL, BC -ADD HL, DE -ADD HL, HL -ADD HL, SP -ADD IX, BC -ADD IX, DE -ADD IX, IX -ADD IX, SP -ADD IY, BC -ADD IY, DE -ADD IY, IY -ADD IY, SP -AND (HL) -AND (IX) -AND (IX+1) -AND (IX-1) -AND (IX+10) -AND (IX-10) -AND (IX+100) -AND (IX-100) -AND (IX+127) -AND (IX-127) -AND (IY) -AND (IY+1) -AND (IY-1) -AND (IY+10) -AND (IY-10) -AND (IY+100) -AND (IY-100) -AND (IY+127) -AND (IY-127) -AND B -AND C -AND D -AND E -AND H -AND L -AND A -AND 1 -AND 2 -AND 4 -AND 8 -AND 16 -AND 32 -AND 64 -AND 128 -BIT 0, (HL) -BIT 0, (IX) -BIT 0, (IX+1) -BIT 0, (IX-1) -BIT 0, (IX+10) -BIT 0, (IX-10) -BIT 0, (IX+100) -BIT 0, (IX-100) -BIT 0, (IX+127) -BIT 0, (IX-127) -BIT 0, (IY) -BIT 0, (IY+1) -BIT 0, (IY-1) -BIT 0, (IY+10) -BIT 0, (IY-10) -BIT 0, (IY+100) -BIT 0, (IY-100) -BIT 0, (IY+127) -BIT 0, (IY-127) -BIT 3, (HL) -BIT 3, (IX) -BIT 3, (IX+1) -BIT 3, (IX-1) -BIT 3, (IX+10) -BIT 3, (IX-10) -BIT 3, (IX+100) -BIT 3, (IX-100) -BIT 3, (IX+127) -BIT 3, (IX-127) -BIT 3, (IY) -BIT 3, (IY+1) -BIT 3, (IY-1) -BIT 3, (IY+10) -BIT 3, (IY-10) -BIT 3, (IY+100) -BIT 3, (IY-100) -BIT 3, (IY+127) -BIT 3, (IY-127) -BIT 7, (HL) -BIT 7, (IX) -BIT 7, (IX+1) -BIT 7, (IX-1) -BIT 7, (IX+10) -BIT 7, (IX-10) -BIT 7, (IX+100) -BIT 7, (IX-100) -BIT 7, (IX+127) -BIT 7, (IX-127) -BIT 7, (IY) -BIT 7, (IY+1) -BIT 7, (IY-1) -BIT 7, (IY+10) -BIT 7, (IY-10) -BIT 7, (IY+100) -BIT 7, (IY-100) -BIT 7, (IY+127) -BIT 7, (IY-127) -BIT 0, B -BIT 0, C -BIT 0, D -BIT 0, E -BIT 0, H -BIT 0, L -BIT 0, A -BIT 3, B -BIT 3, C -BIT 3, D -BIT 3, E -BIT 3, H -BIT 3, L -BIT 3, A -BIT 7, B -BIT 7, C -BIT 7, D -BIT 7, E -BIT 7, H -BIT 7, L -BIT 7, A -CALL Z, 1 -CALL Z, 2 -CALL Z, 4 -CALL Z, 8 -CALL Z, 16 -CALL Z, 32 -CALL Z, 64 -CALL Z, 128 -CALL Z, 256 -CALL Z, 512 -CALL Z, 1024 -CALL Z, 2048 -CALL Z, 4096 -CALL Z, 8192 -CALL Z, 16384 -CALL Z, 32768 -CALL NZ, 1 -CALL NZ, 2 -CALL NZ, 4 -CALL NZ, 8 -CALL NZ, 16 -CALL NZ, 32 -CALL NZ, 64 -CALL NZ, 128 -CALL NZ, 256 -CALL NZ, 512 -CALL NZ, 1024 -CALL NZ, 2048 -CALL NZ, 4096 -CALL NZ, 8192 -CALL NZ, 16384 -CALL NZ, 32768 -CALL C, 1 -CALL C, 2 -CALL C, 4 -CALL C, 8 -CALL C, 16 -CALL C, 32 -CALL C, 64 -CALL C, 128 -CALL C, 256 -CALL C, 512 -CALL C, 1024 -CALL C, 2048 -CALL C, 4096 -CALL C, 8192 -CALL C, 16384 -CALL C, 32768 -CALL NC, 1 -CALL NC, 2 -CALL NC, 4 -CALL NC, 8 -CALL NC, 16 -CALL NC, 32 -CALL NC, 64 -CALL NC, 128 -CALL NC, 256 -CALL NC, 512 -CALL NC, 1024 -CALL NC, 2048 -CALL NC, 4096 -CALL NC, 8192 -CALL NC, 16384 -CALL NC, 32768 -CALL P, 1 -CALL P, 2 -CALL P, 4 -CALL P, 8 -CALL P, 16 -CALL P, 32 -CALL P, 64 -CALL P, 128 -CALL P, 256 -CALL P, 512 -CALL P, 1024 -CALL P, 2048 -CALL P, 4096 -CALL P, 8192 -CALL P, 16384 -CALL P, 32768 -CALL M, 1 -CALL M, 2 -CALL M, 4 -CALL M, 8 -CALL M, 16 -CALL M, 32 -CALL M, 64 -CALL M, 128 -CALL M, 256 -CALL M, 512 -CALL M, 1024 -CALL M, 2048 -CALL M, 4096 -CALL M, 8192 -CALL M, 16384 -CALL M, 32768 -CALL PO, 1 -CALL PO, 2 -CALL PO, 4 -CALL PO, 8 -CALL PO, 16 -CALL PO, 32 -CALL PO, 64 -CALL PO, 128 -CALL PO, 256 -CALL PO, 512 -CALL PO, 1024 -CALL PO, 2048 -CALL PO, 4096 -CALL PO, 8192 -CALL PO, 16384 -CALL PO, 32768 -CALL PE, 1 -CALL PE, 2 -CALL PE, 4 -CALL PE, 8 -CALL PE, 16 -CALL PE, 32 -CALL PE, 64 -CALL PE, 128 -CALL PE, 256 -CALL PE, 512 -CALL PE, 1024 -CALL PE, 2048 -CALL PE, 4096 -CALL PE, 8192 -CALL PE, 16384 -CALL PE, 32768 -CALL 1 -CALL 2 -CALL 4 -CALL 8 -CALL 16 -CALL 32 -CALL 64 -CALL 128 -CALL 256 -CALL 512 -CALL 1024 -CALL 2048 -CALL 4096 -CALL 8192 -CALL 16384 -CALL 32768 -CCF -CP (HL) -CP (IX) -CP (IX+1) -CP (IX-1) -CP (IX+10) -CP (IX-10) -CP (IX+100) -CP (IX-100) -CP (IX+127) -CP (IX-127) -CP (IY) -CP (IY+1) -CP (IY-1) -CP (IY+10) -CP (IY-10) -CP (IY+100) -CP (IY-100) -CP (IY+127) -CP (IY-127) -CP B -CP C -CP D -CP E -CP H -CP L -CP A -CP 1 -CP 2 -CP 4 -CP 8 -CP 16 -CP 32 -CP 64 -CP 128 -CPD -CPDR -CPI -CPIR -CPL -DAA -DEC (HL) -DEC (IX) -DEC (IX+1) -DEC (IX-1) -DEC (IX+10) -DEC (IX-10) -DEC (IX+100) -DEC (IX-100) -DEC (IX+127) -DEC (IX-127) -DEC (IY) -DEC (IY+1) -DEC (IY-1) -DEC (IY+10) -DEC (IY-10) -DEC (IY+100) -DEC (IY-100) -DEC (IY+127) -DEC (IY-127) -DEC IX -DEC IY -DEC B -DEC C -DEC D -DEC E -DEC H -DEC L -DEC A -DEC BC -DEC DE -DEC HL -DEC SP -DI -DJNZ $+1 -DJNZ $+2 -DJNZ $+4 -DJNZ $+8 -DJNZ $+16 -DJNZ $+32 -DJNZ $+64 -DJNZ $-1 -DJNZ $-2 -DJNZ $-4 -DJNZ $-8 -DJNZ $-16 -DJNZ $-32 -DJNZ $-64 -EI -EX (SP), HL -EX (SP), IX -EX (SP), IY -EX AF, AF' -EX DE, HL -EXX -HALT -IM 0 -IM 1 -IM 2 -IN A, (1) -IN A, (2) -IN A, (4) -IN A, (8) -IN A, (16) -IN A, (32) -IN A, (64) -IN A, (128) -IN B, (C) -IN C, (C) -IN D, (C) -IN E, (C) -IN H, (C) -IN L, (C) -IN A, (C) -INC (HL) -INC (IX) -INC (IX+1) -INC (IX-1) -INC (IX+10) -INC (IX-10) -INC (IX+100) -INC (IX-100) -INC (IX+127) -INC (IX-127) -INC (IY) -INC (IY+1) -INC (IY-1) -INC (IY+10) -INC (IY-10) -INC (IY+100) -INC (IY-100) -INC (IY+127) -INC (IY-127) -INC IX -INC IY -INC B -INC C -INC D -INC E -INC H -INC L -INC A -INC BC -INC DE -INC HL -INC SP -IND -INDR -INI -INIR -JP (IX) -JP (IY) -JP (HL) -JP Z, 1 -JP Z, 2 -JP Z, 4 -JP Z, 8 -JP Z, 16 -JP Z, 32 -JP Z, 64 -JP Z, 128 -JP Z, 256 -JP Z, 512 -JP Z, 1024 -JP Z, 2048 -JP Z, 4096 -JP Z, 8192 -JP Z, 16384 -JP Z, 32768 -JP NZ, 1 -JP NZ, 2 -JP NZ, 4 -JP NZ, 8 -JP NZ, 16 -JP NZ, 32 -JP NZ, 64 -JP NZ, 128 -JP NZ, 256 -JP NZ, 512 -JP NZ, 1024 -JP NZ, 2048 -JP NZ, 4096 -JP NZ, 8192 -JP NZ, 16384 -JP NZ, 32768 -JP C, 1 -JP C, 2 -JP C, 4 -JP C, 8 -JP C, 16 -JP C, 32 -JP C, 64 -JP C, 128 -JP C, 256 -JP C, 512 -JP C, 1024 -JP C, 2048 -JP C, 4096 -JP C, 8192 -JP C, 16384 -JP C, 32768 -JP NC, 1 -JP NC, 2 -JP NC, 4 -JP NC, 8 -JP NC, 16 -JP NC, 32 -JP NC, 64 -JP NC, 128 -JP NC, 256 -JP NC, 512 -JP NC, 1024 -JP NC, 2048 -JP NC, 4096 -JP NC, 8192 -JP NC, 16384 -JP NC, 32768 -JP P, 1 -JP P, 2 -JP P, 4 -JP P, 8 -JP P, 16 -JP P, 32 -JP P, 64 -JP P, 128 -JP P, 256 -JP P, 512 -JP P, 1024 -JP P, 2048 -JP P, 4096 -JP P, 8192 -JP P, 16384 -JP P, 32768 -JP M, 1 -JP M, 2 -JP M, 4 -JP M, 8 -JP M, 16 -JP M, 32 -JP M, 64 -JP M, 128 -JP M, 256 -JP M, 512 -JP M, 1024 -JP M, 2048 -JP M, 4096 -JP M, 8192 -JP M, 16384 -JP M, 32768 -JP PO, 1 -JP PO, 2 -JP PO, 4 -JP PO, 8 -JP PO, 16 -JP PO, 32 -JP PO, 64 -JP PO, 128 -JP PO, 256 -JP PO, 512 -JP PO, 1024 -JP PO, 2048 -JP PO, 4096 -JP PO, 8192 -JP PO, 16384 -JP PO, 32768 -JP PE, 1 -JP PE, 2 -JP PE, 4 -JP PE, 8 -JP PE, 16 -JP PE, 32 -JP PE, 64 -JP PE, 128 -JP PE, 256 -JP PE, 512 -JP PE, 1024 -JP PE, 2048 -JP PE, 4096 -JP PE, 8192 -JP PE, 16384 -JP PE, 32768 -JP 1 -JP 2 -JP 4 -JP 8 -JP 16 -JP 32 -JP 64 -JP 128 -JP 256 -JP 512 -JP 1024 -JP 2048 -JP 4096 -JP 8192 -JP 16384 -JP 32768 -JR $+1 -JR $+2 -JR $+4 -JR $+8 -JR $+16 -JR $+32 -JR $+64 -JR $-1 -JR $-2 -JR $-4 -JR $-8 -JR $-16 -JR $-32 -JR $-64 -JR C, $+1 -JR C, $+2 -JR C, $+4 -JR C, $+8 -JR C, $+16 -JR C, $+32 -JR C, $+64 -JR C, $-1 -JR C, $-2 -JR C, $-4 -JR C, $-8 -JR C, $-16 -JR C, $-32 -JR C, $-64 -JR NC, $+1 -JR NC, $+2 -JR NC, $+4 -JR NC, $+8 -JR NC, $+16 -JR NC, $+32 -JR NC, $+64 -JR NC, $-1 -JR NC, $-2 -JR NC, $-4 -JR NC, $-8 -JR NC, $-16 -JR NC, $-32 -JR NC, $-64 -JR Z, $+1 -JR Z, $+2 -JR Z, $+4 -JR Z, $+8 -JR Z, $+16 -JR Z, $+32 -JR Z, $+64 -JR Z, $-1 -JR Z, $-2 -JR Z, $-4 -JR Z, $-8 -JR Z, $-16 -JR Z, $-32 -JR Z, $-64 -JR NZ, $+1 -JR NZ, $+2 -JR NZ, $+4 -JR NZ, $+8 -JR NZ, $+16 -JR NZ, $+32 -JR NZ, $+64 -JR NZ, $-1 -JR NZ, $-2 -JR NZ, $-4 -JR NZ, $-8 -JR NZ, $-16 -JR NZ, $-32 -JR NZ, $-64 -LD (BC), A -LD (DE), A -LD A, (BC) -LD A, (DE) -LD SP, HL -LD A, I -LD I, A -LD A, R -LD R, A -LD (HL), B -LD (HL), C -LD (HL), D -LD (HL), E -LD (HL), H -LD (HL), L -LD (HL), A -LD B, (HL) -LD C, (HL) -LD D, (HL) -LD E, (HL) -LD H, (HL) -LD L, (HL) -LD A, (HL) -LD (HL), 1 -LD (HL), 2 -LD (HL), 4 -LD (HL), 8 -LD (HL), 16 -LD (HL), 32 -LD (HL), 64 -LD (HL), 128 -LD B, 1 -LD B, 2 -LD B, 4 -LD B, 8 -LD B, 16 -LD B, 32 -LD B, 64 -LD B, 128 -LD C, 1 -LD C, 2 -LD C, 4 -LD C, 8 -LD C, 16 -LD C, 32 -LD C, 64 -LD C, 128 -LD D, 1 -LD D, 2 -LD D, 4 -LD D, 8 -LD D, 16 -LD D, 32 -LD D, 64 -LD D, 128 -LD E, 1 -LD E, 2 -LD E, 4 -LD E, 8 -LD E, 16 -LD E, 32 -LD E, 64 -LD E, 128 -LD H, 1 -LD H, 2 -LD H, 4 -LD H, 8 -LD H, 16 -LD H, 32 -LD H, 64 -LD H, 128 -LD L, 1 -LD L, 2 -LD L, 4 -LD L, 8 -LD L, 16 -LD L, 32 -LD L, 64 -LD L, 128 -LD A, 1 -LD A, 2 -LD A, 4 -LD A, 8 -LD A, 16 -LD A, 32 -LD A, 64 -LD A, 128 -LD B, B -LD B, C -LD B, D -LD B, E -LD B, H -LD B, L -LD B, A -LD C, B -LD C, C -LD C, D -LD C, E -LD C, H -LD C, L -LD C, A -LD D, B -LD D, C -LD D, D -LD D, E -LD D, H -LD D, L -LD D, A -LD E, B -LD E, C -LD E, D -LD E, E -LD E, H -LD E, L -LD E, A -LD H, B -LD H, C -LD H, D -LD H, E -LD H, H -LD H, L -LD H, A -LD L, B -LD L, C -LD L, D -LD L, E -LD L, H -LD L, L -LD L, A -LD A, B -LD A, C -LD A, D -LD A, E -LD A, H -LD A, L -LD A, A -LD BC, 1 -LD BC, 2 -LD BC, 4 -LD BC, 8 -LD BC, 16 -LD BC, 32 -LD BC, 64 -LD BC, 128 -LD BC, 256 -LD BC, 512 -LD BC, 1024 -LD BC, 2048 -LD BC, 4096 -LD BC, 8192 -LD BC, 16384 -LD BC, 32768 -LD DE, 1 -LD DE, 2 -LD DE, 4 -LD DE, 8 -LD DE, 16 -LD DE, 32 -LD DE, 64 -LD DE, 128 -LD DE, 256 -LD DE, 512 -LD DE, 1024 -LD DE, 2048 -LD DE, 4096 -LD DE, 8192 -LD DE, 16384 -LD DE, 32768 -LD HL, 1 -LD HL, 2 -LD HL, 4 -LD HL, 8 -LD HL, 16 -LD HL, 32 -LD HL, 64 -LD HL, 128 -LD HL, 256 -LD HL, 512 -LD HL, 1024 -LD HL, 2048 -LD HL, 4096 -LD HL, 8192 -LD HL, 16384 -LD HL, 32768 -LD SP, 1 -LD SP, 2 -LD SP, 4 -LD SP, 8 -LD SP, 16 -LD SP, 32 -LD SP, 64 -LD SP, 128 -LD SP, 256 -LD SP, 512 -LD SP, 1024 -LD SP, 2048 -LD SP, 4096 -LD SP, 8192 -LD SP, 16384 -LD SP, 32768 -LD IX, 1 -LD IX, 2 -LD IX, 4 -LD IX, 8 -LD IX, 16 -LD IX, 32 -LD IX, 64 -LD IX, 128 -LD IX, 256 -LD IX, 512 -LD IX, 1024 -LD IX, 2048 -LD IX, 4096 -LD IX, 8192 -LD IX, 16384 -LD IX, 32768 -LD IY, 1 -LD IY, 2 -LD IY, 4 -LD IY, 8 -LD IY, 16 -LD IY, 32 -LD IY, 64 -LD IY, 128 -LD IY, 256 -LD IY, 512 -LD IY, 1024 -LD IY, 2048 -LD IY, 4096 -LD IY, 8192 -LD IY, 16384 -LD IY, 32768 -LD (1), A -LD (2), A -LD (4), A -LD (8), A -LD (16), A -LD (32), A -LD (64), A -LD (128), A -LD (256), A -LD (512), A -LD (1024), A -LD (2048), A -LD (4096), A -LD (8192), A -LD (16384), A -LD (32768), A -LD A, (1) -LD A, (2) -LD A, (4) -LD A, (8) -LD A, (16) -LD A, (32) -LD A, (64) -LD A, (128) -LD A, (256) -LD A, (512) -LD A, (1024) -LD A, (2048) -LD A, (4096) -LD A, (8192) -LD A, (16384) -LD A, (32768) -LD (1), HL -LD (2), HL -LD (4), HL -LD (8), HL -LD (16), HL -LD (32), HL -LD (64), HL -LD (128), HL -LD (256), HL -LD (512), HL -LD (1024), HL -LD (2048), HL -LD (4096), HL -LD (8192), HL -LD (16384), HL -LD (32768), HL -LD HL, (1) -LD HL, (2) -LD HL, (4) -LD HL, (8) -LD HL, (16) -LD HL, (32) -LD HL, (64) -LD HL, (128) -LD HL, (256) -LD HL, (512) -LD HL, (1024) -LD HL, (2048) -LD HL, (4096) -LD HL, (8192) -LD HL, (16384) -LD HL, (32768) -LD (1), IX -LD (2), IX -LD (4), IX -LD (8), IX -LD (16), IX -LD (32), IX -LD (64), IX -LD (128), IX -LD (256), IX -LD (512), IX -LD (1024), IX -LD (2048), IX -LD (4096), IX -LD (8192), IX -LD (16384), IX -LD (32768), IX -LD IX, (1) -LD IX, (2) -LD IX, (4) -LD IX, (8) -LD IX, (16) -LD IX, (32) -LD IX, (64) -LD IX, (128) -LD IX, (256) -LD IX, (512) -LD IX, (1024) -LD IX, (2048) -LD IX, (4096) -LD IX, (8192) -LD IX, (16384) -LD IX, (32768) -LD (1), IY -LD (2), IY -LD (4), IY -LD (8), IY -LD (16), IY -LD (32), IY -LD (64), IY -LD (128), IY -LD (256), IY -LD (512), IY -LD (1024), IY -LD (2048), IY -LD (4096), IY -LD (8192), IY -LD (16384), IY -LD (32768), IY -LD IY, (1) -LD IY, (2) -LD IY, (4) -LD IY, (8) -LD IY, (16) -LD IY, (32) -LD IY, (64) -LD IY, (128) -LD IY, (256) -LD IY, (512) -LD IY, (1024) -LD IY, (2048) -LD IY, (4096) -LD IY, (8192) -LD IY, (16384) -LD IY, (32768) -LD (1), BC -LD (1), DE -LD (1), HL -LD (1), SP -LD (2), BC -LD (2), DE -LD (2), HL -LD (2), SP -LD (4), BC -LD (4), DE -LD (4), HL -LD (4), SP -LD (8), BC -LD (8), DE -LD (8), HL -LD (8), SP -LD (16), BC -LD (16), DE -LD (16), HL -LD (16), SP -LD (32), BC -LD (32), DE -LD (32), HL -LD (32), SP -LD (64), BC -LD (64), DE -LD (64), HL -LD (64), SP -LD (128), BC -LD (128), DE -LD (128), HL -LD (128), SP -LD (256), BC -LD (256), DE -LD (256), HL -LD (256), SP -LD (512), BC -LD (512), DE -LD (512), HL -LD (512), SP -LD (1024), BC -LD (1024), DE -LD (1024), HL -LD (1024), SP -LD (2048), BC -LD (2048), DE -LD (2048), HL -LD (2048), SP -LD (4096), BC -LD (4096), DE -LD (4096), HL -LD (4096), SP -LD (8192), BC -LD (8192), DE -LD (8192), HL -LD (8192), SP -LD (16384), BC -LD (16384), DE -LD (16384), HL -LD (16384), SP -LD (32768), BC -LD (32768), DE -LD (32768), HL -LD (32768), SP -LD BC, (1) -LD BC, (2) -LD BC, (4) -LD BC, (8) -LD BC, (16) -LD BC, (32) -LD BC, (64) -LD BC, (128) -LD BC, (256) -LD BC, (512) -LD BC, (1024) -LD BC, (2048) -LD BC, (4096) -LD BC, (8192) -LD BC, (16384) -LD BC, (32768) -LD DE, (1) -LD DE, (2) -LD DE, (4) -LD DE, (8) -LD DE, (16) -LD DE, (32) -LD DE, (64) -LD DE, (128) -LD DE, (256) -LD DE, (512) -LD DE, (1024) -LD DE, (2048) -LD DE, (4096) -LD DE, (8192) -LD DE, (16384) -LD DE, (32768) -LD HL, (1) -LD HL, (2) -LD HL, (4) -LD HL, (8) -LD HL, (16) -LD HL, (32) -LD HL, (64) -LD HL, (128) -LD HL, (256) -LD HL, (512) -LD HL, (1024) -LD HL, (2048) -LD HL, (4096) -LD HL, (8192) -LD HL, (16384) -LD HL, (32768) -LD SP, (1) -LD SP, (2) -LD SP, (4) -LD SP, (8) -LD SP, (16) -LD SP, (32) -LD SP, (64) -LD SP, (128) -LD SP, (256) -LD SP, (512) -LD SP, (1024) -LD SP, (2048) -LD SP, (4096) -LD SP, (8192) -LD SP, (16384) -LD SP, (32768) -LD (IX), 1 -LD (IX), 2 -LD (IX), 4 -LD (IX), 8 -LD (IX), 16 -LD (IX), 32 -LD (IX), 64 -LD (IX), 128 -LD (IX+1), 1 -LD (IX+1), 2 -LD (IX+1), 4 -LD (IX+1), 8 -LD (IX+1), 16 -LD (IX+1), 32 -LD (IX+1), 64 -LD (IX+1), 128 -LD (IX-1), 1 -LD (IX-1), 2 -LD (IX-1), 4 -LD (IX-1), 8 -LD (IX-1), 16 -LD (IX-1), 32 -LD (IX-1), 64 -LD (IX-1), 128 -LD (IX+10), 1 -LD (IX+10), 2 -LD (IX+10), 4 -LD (IX+10), 8 -LD (IX+10), 16 -LD (IX+10), 32 -LD (IX+10), 64 -LD (IX+10), 128 -LD (IX-10), 1 -LD (IX-10), 2 -LD (IX-10), 4 -LD (IX-10), 8 -LD (IX-10), 16 -LD (IX-10), 32 -LD (IX-10), 64 -LD (IX-10), 128 -LD (IX+100), 1 -LD (IX+100), 2 -LD (IX+100), 4 -LD (IX+100), 8 -LD (IX+100), 16 -LD (IX+100), 32 -LD (IX+100), 64 -LD (IX+100), 128 -LD (IX-100), 1 -LD (IX-100), 2 -LD (IX-100), 4 -LD (IX-100), 8 -LD (IX-100), 16 -LD (IX-100), 32 -LD (IX-100), 64 -LD (IX-100), 128 -LD (IX+127), 1 -LD (IX+127), 2 -LD (IX+127), 4 -LD (IX+127), 8 -LD (IX+127), 16 -LD (IX+127), 32 -LD (IX+127), 64 -LD (IX+127), 128 -LD (IX-127), 1 -LD (IX-127), 2 -LD (IX-127), 4 -LD (IX-127), 8 -LD (IX-127), 16 -LD (IX-127), 32 -LD (IX-127), 64 -LD (IX-127), 128 -LD (IY), 1 -LD (IY), 2 -LD (IY), 4 -LD (IY), 8 -LD (IY), 16 -LD (IY), 32 -LD (IY), 64 -LD (IY), 128 -LD (IY+1), 1 -LD (IY+1), 2 -LD (IY+1), 4 -LD (IY+1), 8 -LD (IY+1), 16 -LD (IY+1), 32 -LD (IY+1), 64 -LD (IY+1), 128 -LD (IY-1), 1 -LD (IY-1), 2 -LD (IY-1), 4 -LD (IY-1), 8 -LD (IY-1), 16 -LD (IY-1), 32 -LD (IY-1), 64 -LD (IY-1), 128 -LD (IY+10), 1 -LD (IY+10), 2 -LD (IY+10), 4 -LD (IY+10), 8 -LD (IY+10), 16 -LD (IY+10), 32 -LD (IY+10), 64 -LD (IY+10), 128 -LD (IY-10), 1 -LD (IY-10), 2 -LD (IY-10), 4 -LD (IY-10), 8 -LD (IY-10), 16 -LD (IY-10), 32 -LD (IY-10), 64 -LD (IY-10), 128 -LD (IY+100), 1 -LD (IY+100), 2 -LD (IY+100), 4 -LD (IY+100), 8 -LD (IY+100), 16 -LD (IY+100), 32 -LD (IY+100), 64 -LD (IY+100), 128 -LD (IY-100), 1 -LD (IY-100), 2 -LD (IY-100), 4 -LD (IY-100), 8 -LD (IY-100), 16 -LD (IY-100), 32 -LD (IY-100), 64 -LD (IY-100), 128 -LD (IY+127), 1 -LD (IY+127), 2 -LD (IY+127), 4 -LD (IY+127), 8 -LD (IY+127), 16 -LD (IY+127), 32 -LD (IY+127), 64 -LD (IY+127), 128 -LD (IY-127), 1 -LD (IY-127), 2 -LD (IY-127), 4 -LD (IY-127), 8 -LD (IY-127), 16 -LD (IY-127), 32 -LD (IY-127), 64 -LD (IY-127), 128 -LD (IX), B -LD (IX), C -LD (IX), D -LD (IX), E -LD (IX), H -LD (IX), L -LD (IX), A -LD (IX+1), B -LD (IX+1), C -LD (IX+1), D -LD (IX+1), E -LD (IX+1), H -LD (IX+1), L -LD (IX+1), A -LD (IX-1), B -LD (IX-1), C -LD (IX-1), D -LD (IX-1), E -LD (IX-1), H -LD (IX-1), L -LD (IX-1), A -LD (IX+10), B -LD (IX+10), C -LD (IX+10), D -LD (IX+10), E -LD (IX+10), H -LD (IX+10), L -LD (IX+10), A -LD (IX-10), B -LD (IX-10), C -LD (IX-10), D -LD (IX-10), E -LD (IX-10), H -LD (IX-10), L -LD (IX-10), A -LD (IX+100), B -LD (IX+100), C -LD (IX+100), D -LD (IX+100), E -LD (IX+100), H -LD (IX+100), L -LD (IX+100), A -LD (IX-100), B -LD (IX-100), C -LD (IX-100), D -LD (IX-100), E -LD (IX-100), H -LD (IX-100), L -LD (IX-100), A -LD (IX+127), B -LD (IX+127), C -LD (IX+127), D -LD (IX+127), E -LD (IX+127), H -LD (IX+127), L -LD (IX+127), A -LD (IX-127), B -LD (IX-127), C -LD (IX-127), D -LD (IX-127), E -LD (IX-127), H -LD (IX-127), L -LD (IX-127), A -LD (IY), B -LD (IY), C -LD (IY), D -LD (IY), E -LD (IY), H -LD (IY), L -LD (IY), A -LD (IY+1), B -LD (IY+1), C -LD (IY+1), D -LD (IY+1), E -LD (IY+1), H -LD (IY+1), L -LD (IY+1), A -LD (IY-1), B -LD (IY-1), C -LD (IY-1), D -LD (IY-1), E -LD (IY-1), H -LD (IY-1), L -LD (IY-1), A -LD (IY+10), B -LD (IY+10), C -LD (IY+10), D -LD (IY+10), E -LD (IY+10), H -LD (IY+10), L -LD (IY+10), A -LD (IY-10), B -LD (IY-10), C -LD (IY-10), D -LD (IY-10), E -LD (IY-10), H -LD (IY-10), L -LD (IY-10), A -LD (IY+100), B -LD (IY+100), C -LD (IY+100), D -LD (IY+100), E -LD (IY+100), H -LD (IY+100), L -LD (IY+100), A -LD (IY-100), B -LD (IY-100), C -LD (IY-100), D -LD (IY-100), E -LD (IY-100), H -LD (IY-100), L -LD (IY-100), A -LD (IY+127), B -LD (IY+127), C -LD (IY+127), D -LD (IY+127), E -LD (IY+127), H -LD (IY+127), L -LD (IY+127), A -LD (IY-127), B -LD (IY-127), C -LD (IY-127), D -LD (IY-127), E -LD (IY-127), H -LD (IY-127), L -LD (IY-127), A -LD B, (IX) -LD B, (IX+1) -LD B, (IX-1) -LD B, (IX+10) -LD B, (IX-10) -LD B, (IX+100) -LD B, (IX-100) -LD B, (IX+127) -LD B, (IX-127) -LD C, (IX) -LD C, (IX+1) -LD C, (IX-1) -LD C, (IX+10) -LD C, (IX-10) -LD C, (IX+100) -LD C, (IX-100) -LD C, (IX+127) -LD C, (IX-127) -LD D, (IX) -LD D, (IX+1) -LD D, (IX-1) -LD D, (IX+10) -LD D, (IX-10) -LD D, (IX+100) -LD D, (IX-100) -LD D, (IX+127) -LD D, (IX-127) -LD E, (IX) -LD E, (IX+1) -LD E, (IX-1) -LD E, (IX+10) -LD E, (IX-10) -LD E, (IX+100) -LD E, (IX-100) -LD E, (IX+127) -LD E, (IX-127) -LD H, (IX) -LD H, (IX+1) -LD H, (IX-1) -LD H, (IX+10) -LD H, (IX-10) -LD H, (IX+100) -LD H, (IX-100) -LD H, (IX+127) -LD H, (IX-127) -LD L, (IX) -LD L, (IX+1) -LD L, (IX-1) -LD L, (IX+10) -LD L, (IX-10) -LD L, (IX+100) -LD L, (IX-100) -LD L, (IX+127) -LD L, (IX-127) -LD A, (IX) -LD A, (IX+1) -LD A, (IX-1) -LD A, (IX+10) -LD A, (IX-10) -LD A, (IX+100) -LD A, (IX-100) -LD A, (IX+127) -LD A, (IX-127) -LD B, (IY) -LD B, (IY+1) -LD B, (IY-1) -LD B, (IY+10) -LD B, (IY-10) -LD B, (IY+100) -LD B, (IY-100) -LD B, (IY+127) -LD B, (IY-127) -LD C, (IY) -LD C, (IY+1) -LD C, (IY-1) -LD C, (IY+10) -LD C, (IY-10) -LD C, (IY+100) -LD C, (IY-100) -LD C, (IY+127) -LD C, (IY-127) -LD D, (IY) -LD D, (IY+1) -LD D, (IY-1) -LD D, (IY+10) -LD D, (IY-10) -LD D, (IY+100) -LD D, (IY-100) -LD D, (IY+127) -LD D, (IY-127) -LD E, (IY) -LD E, (IY+1) -LD E, (IY-1) -LD E, (IY+10) -LD E, (IY-10) -LD E, (IY+100) -LD E, (IY-100) -LD E, (IY+127) -LD E, (IY-127) -LD H, (IY) -LD H, (IY+1) -LD H, (IY-1) -LD H, (IY+10) -LD H, (IY-10) -LD H, (IY+100) -LD H, (IY-100) -LD H, (IY+127) -LD H, (IY-127) -LD L, (IY) -LD L, (IY+1) -LD L, (IY-1) -LD L, (IY+10) -LD L, (IY-10) -LD L, (IY+100) -LD L, (IY-100) -LD L, (IY+127) -LD L, (IY-127) -LD A, (IY) -LD A, (IY+1) -LD A, (IY-1) -LD A, (IY+10) -LD A, (IY-10) -LD A, (IY+100) -LD A, (IY-100) -LD A, (IY+127) -LD A, (IY-127) -LDD -LDDR -LDI -LDIR -NEG -NOP -OR (HL) -OR (IX) -OR (IX+1) -OR (IX-1) -OR (IX+10) -OR (IX-10) -OR (IX+100) -OR (IX-100) -OR (IX+127) -OR (IX-127) -OR (IY) -OR (IY+1) -OR (IY-1) -OR (IY+10) -OR (IY-10) -OR (IY+100) -OR (IY-100) -OR (IY+127) -OR (IY-127) -OR B -OR C -OR D -OR E -OR H -OR L -OR A -OR 1 -OR 2 -OR 4 -OR 8 -OR 16 -OR 32 -OR 64 -OR 128 -OTDR -OTIR -OUT (1), A -OUT (2), A -OUT (4), A -OUT (8), A -OUT (16), A -OUT (32), A -OUT (64), A -OUT (128), A -OUT (C), B -OUT (C), C -OUT (C), D -OUT (C), E -OUT (C), H -OUT (C), L -OUT (C), A -POP IX -POP IY -POP BC -POP DE -POP HL -POP AF -PUSH IX -PUSH IY -PUSH BC -PUSH DE -PUSH HL -PUSH AF -RES 0, (HL) -RES 0, (IX) -RES 0, (IX+1) -RES 0, (IX-1) -RES 0, (IX+10) -RES 0, (IX-10) -RES 0, (IX+100) -RES 0, (IX-100) -RES 0, (IX+127) -RES 0, (IX-127) -RES 0, (IY) -RES 0, (IY+1) -RES 0, (IY-1) -RES 0, (IY+10) -RES 0, (IY-10) -RES 0, (IY+100) -RES 0, (IY-100) -RES 0, (IY+127) -RES 0, (IY-127) -RES 3, (HL) -RES 3, (IX) -RES 3, (IX+1) -RES 3, (IX-1) -RES 3, (IX+10) -RES 3, (IX-10) -RES 3, (IX+100) -RES 3, (IX-100) -RES 3, (IX+127) -RES 3, (IX-127) -RES 3, (IY) -RES 3, (IY+1) -RES 3, (IY-1) -RES 3, (IY+10) -RES 3, (IY-10) -RES 3, (IY+100) -RES 3, (IY-100) -RES 3, (IY+127) -RES 3, (IY-127) -RES 7, (HL) -RES 7, (IX) -RES 7, (IX+1) -RES 7, (IX-1) -RES 7, (IX+10) -RES 7, (IX-10) -RES 7, (IX+100) -RES 7, (IX-100) -RES 7, (IX+127) -RES 7, (IX-127) -RES 7, (IY) -RES 7, (IY+1) -RES 7, (IY-1) -RES 7, (IY+10) -RES 7, (IY-10) -RES 7, (IY+100) -RES 7, (IY-100) -RES 7, (IY+127) -RES 7, (IY-127) -RES 0, B -RES 0, C -RES 0, D -RES 0, E -RES 0, H -RES 0, L -RES 0, A -RES 3, B -RES 3, C -RES 3, D -RES 3, E -RES 3, H -RES 3, L -RES 3, A -RES 7, B -RES 7, C -RES 7, D -RES 7, E -RES 7, H -RES 7, L -RES 7, A -RET -RET Z -RET NZ -RET C -RET NC -RET P -RET M -RET PO -RET PE -RETI -RETN -RL B -RL C -RL D -RL E -RL H -RL L -RL A -RL (HL) -RL (IX) -RL (IX+1) -RL (IX-1) -RL (IX+10) -RL (IX-10) -RL (IX+100) -RL (IX-100) -RL (IX+127) -RL (IX-127) -RL (IY) -RL (IY+1) -RL (IY-1) -RL (IY+10) -RL (IY-10) -RL (IY+100) -RL (IY-100) -RL (IY+127) -RL (IY-127) -RLA -RLC B -RLC C -RLC D -RLC E -RLC H -RLC L -RLC A -RLCA -RR B -RR C -RR D -RR E -RR H -RR L -RR A -RR (HL) -RR (IX) -RR (IX+1) -RR (IX-1) -RR (IX+10) -RR (IX-10) -RR (IX+100) -RR (IX-100) -RR (IX+127) -RR (IX-127) -RR (IY) -RR (IY+1) -RR (IY-1) -RR (IY+10) -RR (IY-10) -RR (IY+100) -RR (IY-100) -RR (IY+127) -RR (IY-127) -RRA -RRC B -RRC C -RRC D -RRC E -RRC H -RRC L -RRC A -RRCA -RST 0 -RST 8 -RST 16 -RST 24 -RST 32 -RST 40 -RST 48 -RST 56 -SBC A, (HL) -SBC A, B -SBC A, C -SBC A, D -SBC A, E -SBC A, H -SBC A, L -SBC A, A -SBC HL, BC -SBC HL, DE -SBC HL, HL -SBC HL, SP -SCF -SET 0, (HL) -SET 0, (IX) -SET 0, (IX+1) -SET 0, (IX-1) -SET 0, (IX+10) -SET 0, (IX-10) -SET 0, (IX+100) -SET 0, (IX-100) -SET 0, (IX+127) -SET 0, (IX-127) -SET 0, (IY) -SET 0, (IY+1) -SET 0, (IY-1) -SET 0, (IY+10) -SET 0, (IY-10) -SET 0, (IY+100) -SET 0, (IY-100) -SET 0, (IY+127) -SET 0, (IY-127) -SET 3, (HL) -SET 3, (IX) -SET 3, (IX+1) -SET 3, (IX-1) -SET 3, (IX+10) -SET 3, (IX-10) -SET 3, (IX+100) -SET 3, (IX-100) -SET 3, (IX+127) -SET 3, (IX-127) -SET 3, (IY) -SET 3, (IY+1) -SET 3, (IY-1) -SET 3, (IY+10) -SET 3, (IY-10) -SET 3, (IY+100) -SET 3, (IY-100) -SET 3, (IY+127) -SET 3, (IY-127) -SET 7, (HL) -SET 7, (IX) -SET 7, (IX+1) -SET 7, (IX-1) -SET 7, (IX+10) -SET 7, (IX-10) -SET 7, (IX+100) -SET 7, (IX-100) -SET 7, (IX+127) -SET 7, (IX-127) -SET 7, (IY) -SET 7, (IY+1) -SET 7, (IY-1) -SET 7, (IY+10) -SET 7, (IY-10) -SET 7, (IY+100) -SET 7, (IY-100) -SET 7, (IY+127) -SET 7, (IY-127) -SET 0, B -SET 0, C -SET 0, D -SET 0, E -SET 0, H -SET 0, L -SET 0, A -SET 3, B -SET 3, C -SET 3, D -SET 3, E -SET 3, H -SET 3, L -SET 3, A -SET 7, B -SET 7, C -SET 7, D -SET 7, E -SET 7, H -SET 7, L -SET 7, A -SLA B -SLA C -SLA D -SLA E -SLA H -SLA L -SLA A -SRL B -SRL C -SRL D -SRL E -SRL H -SRL L -SRL A -SRL (HL) -SRL (IX) -SRL (IX+1) -SRL (IX-1) -SRL (IX+10) -SRL (IX-10) -SRL (IX+100) -SRL (IX-100) -SRL (IX+127) -SRL (IX-127) -SRL (IY) -SRL (IY+1) -SRL (IY-1) -SRL (IY+10) -SRL (IY-10) -SRL (IY+100) -SRL (IY-100) -SRL (IY+127) -SRL (IY-127) -SUB (HL) -SUB B -SUB C -SUB D -SUB E -SUB H -SUB L -SUB A -SUB 1 -SUB 2 -SUB 4 -SUB 8 -SUB 16 -SUB 32 -SUB 64 -SUB 128 -XOR (HL) -XOR B -XOR C -XOR D -XOR E -XOR H -XOR L -XOR A -XOR 1 -XOR 2 -XOR 4 -XOR 8 -XOR 16 -XOR 32 -XOR 64 -XOR 128 diff --git a/tests/zasm/allinstrs.asm.expected b/tests/zasm/allinstrs.asm.expected deleted file mode 100644 index 7a16f49..0000000 Binary files a/tests/zasm/allinstrs.asm.expected and /dev/null differ diff --git a/tests/zasm/errtests.sh b/tests/zasm/errtests.sh deleted file mode 100755 index 54bfd1f..0000000 --- a/tests/zasm/errtests.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh - -# no "set -e" because we test errors - -ZASM=../../emul/zasm/zasm - -chkerr() { - echo "Check that '$1' results in error $2" - ${ZASM} > /dev/null < 8192 bytes. Large enough to smash the pool. - local i=0 - while [ "$i" -lt "300" ]; do - echo ".equ abcdefghijklmnopqrstuvwxyz$i 42" >> ${tmp} - i=$(($i+1)) - done - ${ZASM} < ${tmp} > /dev/null - local res=$? - rm ${tmp} - if [ $res = 23 ]; then - echo "Good!" - else - echo "$res != 23" - exit 1 - fi -} - -chkerr "foo" 17 -chkerr "ld a, foo" 18 -chkerr "ld a, hl" 18 -chkerr ".db foo" 18 -chkerr ".dw foo" 18 -chkerr ".equ foo bar" 18 -chkerr ".org foo" 18 -chkerr ".fill foo" 18 -chkerr "ld a," 19 -chkerr "ld a, 'A" 19 -chkerr ".db 0x42," 19 -chkerr ".dw 0x4242," 19 -chkerr ".equ" 19 -chkerr ".equ foo" 19 -chkerr ".org" 19 -chkerr ".fill" 19 -chkerr ".inc" 19 -chkerr ".inc foo" 19 -chkerr "ld a, 0x100" 20 -chkerr ".db 0x100" 20 -# TODO: find out why this tests fails on Travis but not on my machine... -# chkerr $'nop \ nop \ nop\n.fill 2-$' 20 -chkerr ".inc \"doesnotexist\"" 21 -chkerr 'foo:\\foo:' 22 -chkoom diff --git a/tests/zasm/geninstrs.py b/tests/zasm/geninstrs.py deleted file mode 100755 index 0acf892..0000000 --- a/tests/zasm/geninstrs.py +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env python3 -# Generate almost all possible combination for instructions from instruction -# tables -# When zasm supported instructions change, use this script to update -# allinstrs.asm - -import sys - -argspecTbl = { - 'A': "A", - 'B': "B", - 'C': "C", - 'k': "(C)", - 'D': "D", - 'E': "E", - 'H': "H", - 'L': "L", - 'I': "I", - 'R': "R", - 'h': "HL", - 'l': "(HL)", - 'd': "DE", - 'e': "(DE)", - 'b': "BC", - 'c': "(BC)", - 'a': "AF", - 'f': "AF'", - 'X': "IX", - 'x': "(IX)", - 'Y': "IY", - 'y': "(IY)", - 's': "SP", - 'p': "(SP)", - 'Z': "Z", - 'z': "NZ", - '=': "NC", - '+': "P", - '-': "M", - '1': "PO", - '2': "PE", -} - -argGrpTbl = { - chr(0x01): "bdha", - chr(0x02): "ZzC=", - chr(0x03): "bdhs", - chr(0x04): "bdXs", - chr(0x05): "bdYs", - chr(0x0a): "ZzC=+-12", - chr(0x0b): "BCDEHLA", -} - -# whenever we encounter the "(HL)" version of these instructions, spit IX/IY -# too. -instrsWithIXY = { - 'ADD', 'AND', 'BIT', 'CP', 'DEC', 'INC', 'OR', 'RES', 'RL', 'RR', 'SET', - 'SRL'} - -def cleanupLine(line): - line = line.strip() - idx = line.rfind(';') - if idx >= 0: - line = line[:idx] - return line - -def getDbLines(fp, tblname): - lookingFor = f"{tblname}:" - line = fp.readline() - while line: - line = cleanupLine(line) - if line == lookingFor: - break - line = fp.readline() - else: - raise Exception(f"{tblname} not found") - - result = [] - line = fp.readline() - while line: - line = cleanupLine(line) - if line == '.db 0xff': - break - # skip index labels lines - if line.startswith('.db'): - result.append([s.strip() for s in line[4:].split(',')]) - line = fp.readline() - return result - -def genargs(argspec): - if not argspec: - return '' - if not isinstance(argspec, str): - argspec = chr(argspec) - if argspec in 'nmNM': - bits = 16 if argspec in 'NM' else 8 - nbs = [str(1 << i) for i in range(bits)] - if argspec in 'mM': - nbs = [f"({n})" for n in nbs] - return nbs - if argspec in 'xy': - # IX/IY displacement is special - base = argspecTbl[argspec] - result = [base] - argspec = argspec.upper() - for n in [1, 10, 100, 127]: - result.append(f"(I{argspec}+{n})") - result.append(f"(I{argspec}-{n})") - return result - if argspec in argspecTbl: - return [argspecTbl[argspec]] - if argspec == chr(0xc): # special BIT "b" group - return ['0', '3', '7'] - grp = argGrpTbl[argspec] - return [argspecTbl[a] for a in grp] - -# process a 'n' arg into an 'e' one -def eargs(args): - newargs = ['$+'+s for s in args[:-1]] - return newargs + ['$-'+s for s in args[:-1]] - -def main(): - asmfile = sys.argv[1] - with open(asmfile, 'rt') as fp: - instrTbl = getDbLines(fp, 'instrTBl') - for row in instrTbl: - n = row[0][2:] # remove I_ - # we need to adjust for zero-char name filling - a1 = eval(row[1]) - a2 = eval(row[2]) - args1 = genargs(a1) - # special case handling - if n in instrsWithIXY and a1 == 'l': - args1 += genargs('x') - args1 += genargs('y') - - if n == 'JP' and isinstance(a1, str) and a1 in 'xy': - # we don't test the displacements for IX/IY because there can't be - # any. - args1 = args1[:1] - if n in {'JR', 'DJNZ'} and a1 == 'n': - args1 = eargs(args1) - if n == 'IM': - args1 = [0, 1, 2] - if n == 'RST': - args1 = [i*8 for i in range(8)] - if args1: - for arg1 in args1: - args2 = genargs(a2) - if n in instrsWithIXY and a2 == 'l': - args2 += genargs('x') - args2 += genargs('y') - if args2: - if n in {'JR', 'DJNZ'} and a2 == 'n': - args2 = eargs(args2) - for arg2 in args2: - print(f"{n} {arg1}, {arg2}") - else: - print(f"{n} {arg1}") - else: - print(n) - pass - -if __name__ == '__main__': - main() diff --git a/tests/zasm/runtests.sh b/tests/zasm/runtests.sh deleted file mode 100755 index aa8edc8..0000000 --- a/tests/zasm/runtests.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -e - -BASE=../.. -KERNEL="${BASE}/kernel" -APPS="${BASE}/apps" -ZASM="${BASE}/emul/zasm/zasm" - -cmpas() { - FN=$1 - EXPECTED=$(xxd ${FN}.expected) - ACTUAL=$(cat ${FN} | $ZASM "${KERNEL}" "${APPS}" | xxd) - if [ "$ACTUAL" = "$EXPECTED" ]; then - echo ok - else - echo actual - echo "$ACTUAL" - echo expected - echo "$EXPECTED" - exit 1 - fi -} - -if [ ! -z $1 ]; then - cmpas $1 - exit 0 -fi - -for fn in *.asm; do - echo "Comparing ${fn}" - cmpas $fn -done - -./errtests.sh diff --git a/tests/zasm/test1.asm b/tests/zasm/test1.asm deleted file mode 100644 index 25aa9b0..0000000 --- a/tests/zasm/test1.asm +++ /dev/null @@ -1,30 +0,0 @@ - ; comment - add a, b ; comment -label1: - inc a ; comment - ld hl, label2 - .dw label2 - ; comment - .db 42, 54 -label2: .dw 0x42 - .dw 3742, 0xffff - .dw 0x3742 - ld a, (label1) - rla \ rla -.equ foo 0x1234 -.equ bar foo - ld hl, bar - ld ix, 1234 - ld iy, 2345 - ld (ix+1), l - ld l, (ix-1) - ld hl, 0x100 -.equ baz 0x20 - ; accept space before comma - ld b , baz-3 - rl d - rr e - rlc c - cp '-' - sbc hl, de -.fill 0x50-$ diff --git a/tests/zasm/test1.asm.expected b/tests/zasm/test1.asm.expected deleted file mode 100644 index ca15e7e..0000000 Binary files a/tests/zasm/test1.asm.expected and /dev/null differ diff --git a/tests/zasm/test10.asm b/tests/zasm/test10.asm deleted file mode 100644 index e881084..0000000 --- a/tests/zasm/test10.asm +++ /dev/null @@ -1,3 +0,0 @@ -inc a -.bin "err.h" -inc b diff --git a/tests/zasm/test10.asm.expected b/tests/zasm/test10.asm.expected deleted file mode 100644 index 1133897..0000000 --- a/tests/zasm/test10.asm.expected +++ /dev/null @@ -1,15 +0,0 @@ -<; Error codes used throughout the kernel - -; The command that was type isn't known to the shell -.equ SHELL_ERR_UNKNOWN_CMD 0x01 - -; Arguments for the command weren't properly formatted -.equ SHELL_ERR_BAD_ARGS 0x02 - -.equ BLOCKDEV_ERR_OUT_OF_BOUNDS 0x03 -.equ BLOCKDEV_ERR_UNSUPPORTED 0x04 - -; IO routines (GetB, PutB) returned an error in a load/save command -.equ SHELL_ERR_IO_ERROR 0x05 - - \ No newline at end of file diff --git a/tests/zasm/test2.asm b/tests/zasm/test2.asm deleted file mode 100644 index 744982f..0000000 --- a/tests/zasm/test2.asm +++ /dev/null @@ -1,11 +0,0 @@ -; Test relative jumps -label1: - jp label1 - jp label2 - jp label2 - jr label2 - jr nc, label1 - -label2: - jr label1 - jr nc, label1 diff --git a/tests/zasm/test2.asm.expected b/tests/zasm/test2.asm.expected deleted file mode 100644 index 2cec424..0000000 Binary files a/tests/zasm/test2.asm.expected and /dev/null differ diff --git a/tests/zasm/test3.asm b/tests/zasm/test3.asm deleted file mode 100644 index f985512..0000000 --- a/tests/zasm/test3.asm +++ /dev/null @@ -1,19 +0,0 @@ -; test local labels -addDE: - push af - add a, e - jr nc, .end ; no carry? skip inc - inc d -.end: ld e, a - pop af - ret - -addHL: - push af - add a, l - jr nc, .end ; no carry? skip inc - inc h -.end: - ld l, a - pop af - ret diff --git a/tests/zasm/test3.asm.expected b/tests/zasm/test3.asm.expected deleted file mode 100644 index 08a7318..0000000 --- a/tests/zasm/test3.asm.expected +++ /dev/null @@ -1 +0,0 @@ -õƒ0_ņÉõ…0$oņÉ \ No newline at end of file diff --git a/tests/zasm/test4.asm b/tests/zasm/test4.asm deleted file mode 100644 index 3fd6852..0000000 --- a/tests/zasm/test4.asm +++ /dev/null @@ -1,10 +0,0 @@ -; test literals parsing - -ld a, 42 -ld a, 0x42 -ld hl, 0x4234 -ld hl, (0x4234) -ld a, 'X' -ld a, ' ' -ld a, 'a' ; don't upcase! -.db "bonjour allo", 0 diff --git a/tests/zasm/test4.asm.expected b/tests/zasm/test4.asm.expected deleted file mode 100644 index 6c6b3f3..0000000 Binary files a/tests/zasm/test4.asm.expected and /dev/null differ diff --git a/tests/zasm/test5.asm b/tests/zasm/test5.asm deleted file mode 100644 index 8d5326f..0000000 --- a/tests/zasm/test5.asm +++ /dev/null @@ -1,4 +0,0 @@ -; test expressions -ld a, 'A'+3 -ld a, 'A'-0x20 -ld a, 8*5 diff --git a/tests/zasm/test5.asm.expected b/tests/zasm/test5.asm.expected deleted file mode 100644 index 4279a95..0000000 --- a/tests/zasm/test5.asm.expected +++ /dev/null @@ -1 +0,0 @@ ->D>!>( \ No newline at end of file diff --git a/tests/zasm/test6.asm b/tests/zasm/test6.asm deleted file mode 100644 index d91189e..0000000 --- a/tests/zasm/test6.asm +++ /dev/null @@ -1,6 +0,0 @@ -; There was a very nasty bug (cost me a couple of hours of mis-debugging) where -; ld r, (iy+d) would use the 0xdd byte code instead of the correct 0xfd one. -; genallinstrs didn't catch it because it outputs uppercase. Oh sweet mother, -; how much time did I lose over this... - -ld h, (iy+1) diff --git a/tests/zasm/test6.asm.expected b/tests/zasm/test6.asm.expected deleted file mode 100644 index ca54089..0000000 --- a/tests/zasm/test6.asm.expected +++ /dev/null @@ -1 +0,0 @@ -ýf \ No newline at end of file diff --git a/tests/zasm/test7.asm b/tests/zasm/test7.asm deleted file mode 100644 index c5976ad..0000000 --- a/tests/zasm/test7.asm +++ /dev/null @@ -1,5 +0,0 @@ -; It's fine to declare the same constant twice. Only the first value is -; kept -.equ FOO 42 -.equ FOO 22 -ld a, FOO diff --git a/tests/zasm/test7.asm.expected b/tests/zasm/test7.asm.expected deleted file mode 100644 index 29cc4ea..0000000 --- a/tests/zasm/test7.asm.expected +++ /dev/null @@ -1 +0,0 @@ ->* \ No newline at end of file diff --git a/tests/zasm/test8.asm b/tests/zasm/test8.asm deleted file mode 100644 index 9962b8e..0000000 --- a/tests/zasm/test8.asm +++ /dev/null @@ -1,6 +0,0 @@ -; test .org directive -.equ foo 1234 -.org foo -label1: - jp label1 - jr label1 diff --git a/tests/zasm/test8.asm.expected b/tests/zasm/test8.asm.expected deleted file mode 100644 index 66fb303..0000000 --- a/tests/zasm/test8.asm.expected +++ /dev/null @@ -1 +0,0 @@ -ÃŌû \ No newline at end of file diff --git a/tests/zasm/test9.asm b/tests/zasm/test9.asm deleted file mode 100644 index a10bbda..0000000 --- a/tests/zasm/test9.asm +++ /dev/null @@ -1,11 +0,0 @@ -; test some weird label bug zasm had at some point. Simply to refer to a local -; label in a .dw directive would mess up future label references. -foo: - inc a -.bar: - inc b -.baz: - .dw .bar - -loop: - jr loop diff --git a/tests/zasm/test9.asm.expected b/tests/zasm/test9.asm.expected deleted file mode 100644 index 19baa68..0000000 Binary files a/tests/zasm/test9.asm.expected and /dev/null differ