|
#test <name>,<start>,<size>
Define code segment for automated tests after successful assembly.
The test code will be loaded on top of the assembled code and executed. During execution i/o addresses are monitored and compared with supplied values. Finally – or at any time during the test – registers and the cpu cycle count can be compared against expected values.
There can be any number of tests in a test segment and there can be any number of test segments which are loaded and executed separately. Test errors are handled like assembly errors. Only Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif all tests pass the assembled code is finally written.
zasm can emulate the 8080, Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 and Command Line Options: --z180 Pseudo instructions: .z80, .z180 and .8080Z180 cpu.
The instruction Pseudo instructions: defl, set and '=' Labels: SETset is limited to the selected CPU, eventually modified by Differences from v3 to v4: Command line options Command Line Options Command Line Options: Command line optionscommand line options --Command Line Options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ Commands for command line options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ixcbr2 or --Command Line Options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ Commands for command line options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ixcbxh. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf an unimplemented / illegal instruction is executed, the emulation stops with an error and the test fails.
The 8080 cpu has no unhandled/undocumented opcodes, but some opcodes have aliases which were used by the Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 for additional functions, e.g. relative jumps, 2nd register Pseudo instructions: defl, set and '=' Labels: SETset and prefix opcodes. Only the official opcodes, which are also those which are generated by zasm, are allowed. Execution of an alias opcode, which was reused by the Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80, is handled like an illegal opcode and will terminate the test. This is to prevent you from accidentially writing code which will not run on a Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80.
- Widely used undocumented opcodes are always executed without generating an error.
These are SLL and access to the upper and lower half of index registers in LD and arithmetical/logical operations.
- Illegal index register opcodes after after prefix IX/IY + prefix 0xCB are allowed, Assembler directives: #if, #elif, #else, #endif
Pseudo instructions: if, endifif the corresponding Differences from v3 to v4: Command line options Command Line Options Command Line Options: Command line optionscommand line option --Command Line Options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ Commands for command line options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ixcbr2 or --Command Line Options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ Commands for command line options: --ixcbxh, .ixcbxh, _ixcbxh_ --ixcbr2, .ixcbr2, _ixcbr2_ixcbxh was Pseudo instructions: defl, set and '=' Labels: SETset. Then they are performed according to this setting. [TODO: the timing of ixcbih opcodes is unclear]
- All other undocumented / illegal opcodes are rejected and will terminate the test with an error.
All Command Line Options: --z180 Pseudo instructions: .z80, .z180 and .8080Z180 opcodes are emulated and the different timing is implemented. The Command Line Options: --z180 Pseudo instructions: .z80, .z180 and .8080Z180 traps illegal instructions and so does the emulation. [TODO: the exact behavior of the SLP opcode is unclear]
The test code is executed by a variant of Kio's Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 engine. The emulation speed is approx. 800MHz@Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 per GHz@Host (2.6GHz on 3.2GHz AMD Ryzen5 2400G) or let's say, depending on the emulated system and the speed of your host, the emulated Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 is running 1000 times faster than the original. :-) I found out that the emulation speed is very sensitive to the loading position of the Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 engine and can vary widely - for my PC from 1MHz emulated speed to 2.5MHz! I'll try to check the speed before releases of zasm but will probably frequenty fail to do so. After all it can be completely different on someone else's PC.
The emulation of the Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 is very precise but i cannot guarantee correctness to the last bit and cycle. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf you find any difference please report a bug. Replication of the undefined behavior of flags is 80% exact.
The assembled code is loaded into 64kB ram, each segment at the address defined in the Assembler directives: #code Including C Source Files: #code#code directive, ignoring possible overlap. Finally the test segment is loaded, again happily overwriting already loaded segments.
- Memory mapping is not supported.
- Wait states are not supported.
- Memory mapped i/o is not supported.
The emulation can be run at maximum speed or limited to a fixed frequency.
A timer interrupt can be enabled and used in multiple ways:
- A timer interrupt after a fixed number of cpu cycles (cc)
- A timer interrupt with a given frequency when the cpu is running at a fixed speed
- A timer interrupt with a given frequency when the cpu is running at maximum speed
- Other interrupt sources are not supported.
- The non-maskable interrupt NMI is not supported.
A timeout for a test can be specified in realtime milliseconds.
In and Out access is monitored and must be prepared for. Access to an unprepared address immediately terminates the test.
I/o addresses can be prepared in multiple ways:
- Assign an address to the console (stdin+stdout)
- provide input data for an In address
- provide test data for an Out address
Not yet supported in zasm v4.4.4
- Loading of a rom file
- read/write/compare to file
#test <name>, <addr>, <size>
;
.test-clock <frequency>
.test-int <frequency> [, <duration>]
.test-int <cpu_cycles> [, <duration>]
.test-intack <int_ack_byte>
.test-timeout <duration>
;
.test-in <addr>, <values>
.test-out <addr>, <expected_values>
.test-console <addr>
Define a fixed speed for the emulation. The emulation will be throttled to the given frequency. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf .test-clock is not specified or Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif the clock is Pseudo instructions: defl, set and '=' Labels: SETset to 0 then the test runs at the fastest possible speed.
.test-clock 4000000 Hz ; run at 4 MHz. mind the space!
.test-clock 3500 kHz ; run at 3.5 MHz. mind the space!
.test-clock 6 MHz ; run at 6 MHz. mind the space!
Enable a timer interrupt and define the interrupt frequency. The frequency can be given in cpu cycles 'cc' or in real-world frequency. Depending on whether the cpu is running at a fixed speed or unlimited speed the executed cpu cycles per interrupt are predictable or may vary widely.
.test-int 50 Hz ; interrupt with real-world frequency
.test-int 70000 cc ; interrupt after 70000 cc (50 Hz at 3.5 MHz: the ZX Spectrum :-)
.test-int 50 ; interrupt with 50 Hz
.test-int 70000 ; interrupt after 70000 cc
The units cc or Hz are optional. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf present, a sanity test is included. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf the unit is omitted, zasm uses values up to 1000 as Hz and larger values as cc. 1000 is the highest value which can be used for frequency.
Since version 4.0.3 a second argument can be added to specify the duration, for which the interrupt should be asserted. This can be used to test whether interrupts are missed due to longisch sequences with interrupts disabled or whether they interrupt itself, Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif enabled in the interrupt handler too early.
It is recommended to use at least 32 cc for the duration, because even Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif your code never disables interrupts (which will easily take longer than that) the longest Command Line Options: --z80 Pseudo instructions: .z80, .z180 and .8080 Targets: #target Z80Z80 opcodes takes 23 cpu cycles! And, of course, best use the actual duration of your real system. :-)
Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf no interrupt duration is specified, then the interrupt is asserted until it is acknowledged (handled).
.test-int 50 Hz, 40 cc ; interrupt with real-world frequency, asserted for 40 cc
.test-int 70000 cc, 48 cc ; interrupt after 70000 cc, asserted for 48 cc
.test-int 50, 128 ; interrupt with 50 Hz, asserted for 128 cc
.test-int 70000, 64 cc ; interrupt after 70000 cc, asserted for 64 cc
There is one more subtable difference between auto-off mode and int-with-duration mode: In auto-off mode the first interrupt, which would occur at cpu cycle cc = 0 is suppressed. In interrupt-with-duration mode the first interrupt at cc = 0 is not suppressed, and Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif the test code enables interrupts very early, then the first interrupt is immediately taken.
Define byte to be read from the bus in an interrupt acknowldge cycle. This byte is 0xFF by default (floating bus on a TTL system) but different values can be defined as provided by some peripheral hardware, to supply a RST opcode or an index in the interrupt vector table, depending on the current interrupt mode of the cpu.
.test-intack 255-8 ; this would be a RST 48
.test-intack opcode(rst 48) ; the same, just readable :-)
.test-intack lo(int_vector) ; low byte of memory address of an interrupt vector
; the high byte would go into the i register.
Define a timeout for the test. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf the test takes longer it is aborted and fails. The timeout is measured in realtime. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf no timeout is Pseudo instructions: defl, set and '=' Labels: SETset or the timeout is Pseudo instructions: defl, set and '=' Labels: SETset to 0 then there is no timeout.
.test-timeout 1 s ; timeout after 1 second. mind the space!
.test-timeout 500 ms ; timeout after 500 millisecond. mind the space!
.test-timeout 1 m ; timeout after 1 minute. mind the space!
Attach an i/o address to stdin and stdout. This is an easy way to print messages from the running code. A little more effort must be taken to input text, because the input is not blocking. Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifIf no input is available, then the port reads as 0x00.
.test-console 0xFE ; use port address 0xFE, high byte is ignored
.test-console 0xFFFE ; use port address 0xFFFE, you must use in(c) and out(c)
Supply input data for an input port. The supplied data is read by inputs from this port.
The test fails, Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif the test code reads more data than supplied and it fails Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif there is some unread data left after the test.
.test-in 0xF8, 0,0,0,"Hello world!",13,0,0,0
This prepares port 0xF8 to return 3 times 0x00 (probably meaning 'no data'), then a text and finally again 3 times 0x00. All data must be read by the test code.
.test-in 0xF8, 0,0,0,"Hello world!",13,0,0,0
.test-in 0xF8, 0,0,0,"Here i go again!",13,0,0,0
This prepares port 0xF8 with more data. The input data is combined from multiple definitions.
You can repeat blocks of data:
.test-in 0xF8, {0}*10, "Hello World!", 13, {0}*10
.test-in 0xF8, {0}*10, {"Hello", 32, "World!", 13} * 5, {0}*10
You can add a final block to repeat forever. This won't make the test fail:
.test-in 0xF8, {0}*10, "Hello World!", 13, {0}*
Supply output test data for an output port. The supplied data is compared against data written to this port.
The test fails, Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif a byte written doesn't match the expectation, Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif the test code writes more data than supplied and it fails Assembler directives: #if, #elif, #else, #endif Pseudo instructions: if, endifif there is some unwritten data left after the test.
.test-out 0xF8, "Hello world!",10
This prepares port 0xF8 to Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect a text and a linebreak. All data must be written by the test code.
.test-out 0xF8, "Hello world!",10
.test-out 0xF8, "Here i go again!",10
This prepares port 0xF8 with more data. The output test data is combined from multiple definitions.
You can repeat blocks of data:
.test-out 0xF8, {'-'}*12, 10, "Hello World!", 10, {'-'}*12, 10
.test-out 0xF8, {'-'}*12, 10, {"Hello World!", 10} * 5, {'-'}*12, 10
You can add a final block to repeat forever. This won't make the test fail:
.test-out 0xF8, {"Hello World!",10,}* ; any number of repetitions of this block
.test-out 0xF8, "Hello World!", 10, * ; any data whatever after text
After the test segment is setup, you can add your test code. It starts up with all registers Pseudo instructions: defl, set and '=' Labels: SETset to 0x00 and interrupts disabled. Typically you load some registers and call a function to test. It is recommended to wrap all tests in a Assembler directives: #local, #endlocal, .local and .endlocal Pseudo instructions: #local, #endlocal, .local and .endlocal Including C Source Files: #local, #endlocal, .local and .endlocal#local context so you can reuse Pseudo instructions: Label definition Numeric expressions: Labels 8080 Assembler: Labelslabel names:
Assembler directives: #local, #endlocal, .local and .endlocal Pseudo instructions: #local, #endlocal, .local and .endlocal Including C Source Files: #local, #endlocal, .local and .endlocal#local
ld sp,0
ld a,3
ld de,7
call A_times_DE
;
.Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect a' = 0
.Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect a = 3
.Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect de = 7
.Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect hl = 3*7
.Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect dehl = 7*$10000 + 3*7
.expect cc < 1000
Assembler directives: #local, #endlocal, .local and .endlocal Pseudo instructions: #local, #endlocal, .local and .endlocal Including C Source Files: #local, #endlocal, .local and .endlocal#endlocal
.Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect can be used to test register values.
You can compare the value of any register, register pair or quad register to match an expected value. Ordered comparisons ('>' or '<') are not allowed.
Recognized register names are:
a f b c d e h l a2 f2 b2 c2 d2 e2 h2 l2 a' f' b' c' d' e' h' l'
xh xl yh yl ixh ixl iyh iyl pch pcl sph spl i r
af bc de hl af2 bc2 de2 hl2 af' bc' de' hl' ix iy pc sp
bcde bchl bcix bciy debc dehl deix deiy etc.
im iff1 iff2
.Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpect can also test the cpu cycle counter.
You can compare the value of the cpu cycle counter to match an expected value or use an ordered comparisons '>' … '<'.
The cycle counter is reset after each block of .Run test code and test your expectations: .expect register Run test code and test your expectations: .expect ccexpects, either cc or registers.
.expect cc = 456
.expect cc >= 400
.expect cc > 400
.expect cc <= 500
.expect cc < 500
Hint: assemble your source with Differences from v3 to v4: Command line options Command Line Options Command Line Options: Command line optionscommand line option -uwy to see the generated code and cycle count.
Hint: run tests with Differences from v3 to v4: Command line options Command Line Options Command Line Options: Command line optionscommand line option -v to see more interesting output. B-)
source file
listing
| |