Draw: implement the BASIC 7.0 graphics verbs
GRAPHIC, COLOR, DRAW, BOX, CIRCLE, PAINT, SCALE, SSHAPE, GSHAPE and LOCATE, all against the akbasic_GraphicsBackend record rather than akgl_draw_* directly, so src/runtime_graphics.c includes no SDL and the whole group is testable in a build with no SDL on the machine. The reference lists every one of these as unimplemented, so the semantics come from Commodore BASIC 7.0 rather than from a port, and four places where a modern renderer cannot do what a C128 did are recorded in TODO.md section 5 rather than silently substituted: - CIRCLE is drawn as a polygon of inc-degree segments and akgl_draw_circle is deliberately unused. 7.0's CIRCLE takes two radii, an arc range and a rotation, so the primitive could serve only the fully-defaulted call, and a shape that changed character depending on whether the radii happened to be equal would be worse than one uniformly a polygon. - SSHAPE puts a SHAPE:<n> handle in the string variable rather than the pixels, because a value's string is a fixed 256 bytes and a region is a device surface. GSHAPE refuses a string without that prefix instead of parsing whatever digits it finds and pasting an unrelated slot. - BOX fills on a negative angle; 7.0 puts the fill flag after the rotation, which would make a filled box a seventh argument. - GRAPHIC stores its mode and honours only the one consequence that means anything here -- mode 0 is text -- while still refusing an out-of-range mode, since that is a typo worth catching. PAINT surfaces the flood fill's AKERR_OUTOFBOUNDS as an error rather than success. The device gives up when its span stack runs out having filled *part* of the region, and a program that cannot tell that happened cannot recover from it. Note the shape of that handler: HANDLE sets handled = true on the context, so a FAIL_RETURN from inside the HANDLE block hands the caller something already marked handled, whose FINISH_LOGIC then declines to pass it up and releases it -- the error disappears and PAINT reports success. Flag inside the block, raise after FINISH. COLOR, LOCATE and SCALE need no device on purpose, so a program can set itself up before a host has lent it a renderer. Adds a second golden corpus under tests/language/. The corpus in deps/basicinterpret is a submodule and nothing here may add files to it, but goal 2's new verbs still need the .bas/.txt half of their coverage. Registered under local_ so a failure names which corpus it came from. What it can cover is limited -- these verbs draw rather than print -- so the behaviour that reaches a device is asserted against tests/mockdevice.h instead. 65/65 ctest, clean under -Wall -Wextra, doxygen clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -107,6 +107,7 @@ set(AKBASIC_SOURCES
|
||||
src/runtime.c
|
||||
src/runtime_commands.c
|
||||
src/runtime_functions.c
|
||||
src/runtime_graphics.c
|
||||
src/scanner.c
|
||||
src/sink_stdio.c
|
||||
src/symtab.c
|
||||
@@ -191,6 +192,7 @@ set(AKBASIC_TESTS
|
||||
environment_scope
|
||||
error_codes
|
||||
grammar_leaves
|
||||
graphics_verbs
|
||||
parser_commands
|
||||
parser_expressions
|
||||
runtime_evaluate
|
||||
@@ -269,6 +271,41 @@ if(AKBASIC_GOLDEN_CASES)
|
||||
_set_tests_properties(${AKBASIC_GOLDEN_NAMES} PROPERTIES TIMEOUT 30)
|
||||
endif()
|
||||
|
||||
# The local golden corpus, for verbs the reference never implemented.
|
||||
#
|
||||
# It has to be separate: the corpus above is a submodule and nothing in this
|
||||
# repository may add files to it, but goal 2's new verbs still need the .bas/.txt
|
||||
# half of their coverage. Registered under local_ so a failure says at a glance
|
||||
# which corpus it came from.
|
||||
#
|
||||
# Note what this can and cannot cover. The graphics and sound verbs draw and play
|
||||
# rather than print, so what a golden file sees of them is their *refusals* and
|
||||
# whatever a program can PRINT about the state they changed. The behaviour that
|
||||
# reaches a device is asserted against tests/mockdevice.h instead.
|
||||
file(GLOB_RECURSE AKBASIC_LOCAL_CASES
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/tests"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/tests/language/*.bas"
|
||||
)
|
||||
|
||||
set(AKBASIC_LOCAL_NAMES)
|
||||
foreach(_case IN LISTS AKBASIC_LOCAL_CASES)
|
||||
string(REGEX REPLACE "^language/" "" _name "${_case}")
|
||||
string(REGEX REPLACE "\\.bas$" "" _name "${_name}")
|
||||
string(REPLACE "/" "_" _name "${_name}")
|
||||
_add_test(
|
||||
NAME local_${_name}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DBASIC=$<TARGET_FILE:basic>
|
||||
-DCASE=${CMAKE_CURRENT_SOURCE_DIR}/tests/${_case}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/tests/golden.cmake
|
||||
)
|
||||
list(APPEND AKBASIC_LOCAL_NAMES local_${_name})
|
||||
endforeach()
|
||||
|
||||
if(AKBASIC_LOCAL_NAMES)
|
||||
_set_tests_properties(${AKBASIC_LOCAL_NAMES} PROPERTIES TIMEOUT 30)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Mutation testing.
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user