diff --git a/clib/s/cl_entries b/clib/s/cl_entries
index 170dd4a11dc8e0203c8fde91b60d80fec2775ae5..dafc55d7ab9a82b9468d451f896be79d5580820a 100644
--- a/clib/s/cl_entries
+++ b/clib/s/cl_entries
@@ -213,6 +213,8 @@
 
         Entry   _swi, imported, , unveneered
         Entry   _swix, imported, , unveneered
+        Entry   _vswi, imported, , unveneered
+        Entry   _vswix, imported, , unveneered
 
 ; __va_illegal_arg 0
         END
diff --git a/h/swisheader b/h/swisheader
index 39dd223a85abcea2fd1cac85ee965e342b3aa757..d8e08be03795e551083b358b80993c911795f28a 100644
--- a/h/swisheader
+++ b/h/swisheader
@@ -34,12 +34,18 @@ extern "C"
 {
 #endif
 
+typedef char *__va_list[1];       /* keep in step with <stdarg.h> */
+
 #pragma -v4
 extern int _swi (int swi_no, unsigned int, ...);
 
 extern _kernel_oserror *_swix (int swi_no, unsigned int, ...);
 #pragma -v0
 
+extern int _vswi (int swi_no, unsigned int, __va_list);
+
+extern _kernel_oserror *_vswix (int swi_no, unsigned int, __va_list);
+
 #ifdef __cplusplus
 }
 #endif
@@ -61,9 +67,11 @@ extern _kernel_oserror *_swix (int swi_no, unsigned int, ...);
  * These functions provide a generic method of calling RISC OS SWIs from C or
  * C++.
  *
- * Two functions are provided:
+ * Four functions are provided:
  *     _swi for calling SWIs without setting the X bit
- *     _swix which sets the X bit before calling the SWI.
+ *     _swix which sets the X bit before calling the SWI
+ *     _vswi, like _swi but using a va_list instead of variadic arguments
+ *     _vswix, like _swix but using a va_list instead of variadic arguments.
  *
  * swi_no is the number of the SWI to be called. This should never have the
  * X bit set.
@@ -142,5 +150,10 @@ extern _kernel_oserror *_swix (int swi_no, unsigned int, ...);
  *               &workspace_end, &next);
  *
  *     e = _swix(Wimp_SetExtent, _IN(0)|_BLOCK(1), w, minx, miny, maxx, maxy);
+ *
+ *     va_list ap;
+ *     va_start(ap, flags);
+ *     e = _vswix(swi_no, flags, ap);
+ *     va_end(ap);
  */
 
diff --git a/kernel/s/swiv b/kernel/s/swiv
index ac03fcb060c961509936828c11f815b95314c9a9..c99ef95985a8a7d159062c586dd647b375a8bdc7 100644
--- a/kernel/s/swiv
+++ b/kernel/s/swiv
@@ -39,10 +39,29 @@ $name   MRS     lr, CPSR
 
         EXPORT  |_swix|
         EXPORT  |_swi|
+        EXPORT  |_vswix|
+        EXPORT  |_vswi|
 
     ; tedious static _swi(x) entry handling, to avoid generating dynamic code, and
     ; requiring an expensive XOS_SynchroniseCodeAreas
 
+|_vswix|
+        ORR     r0, r0, #&20000
+        TST     r1, #&FF0                 ; check for use of input regs. 4 to 9, or of block param
+        BNE     vswix_even_more_tedious   ; if so, do full stuff
+
+        SUB     sp, sp, #2*4              ; leave a gap so we can use common bottom half
+        STMDB   sp!, {r1, r4-r9, lr}      ; save stuff
+        LDR     r14, [r2]                 ; r14 -> input args
+        B       swix_vswix_common
+
+vswix_even_more_tedious
+|_vswi|
+        SUB     sp, sp, #2*4              ; leave a gap so we can use common bottom half
+        STMDB   sp!, {r1, r4-r9, lr}      ; save stuff
+        LDR     r14, [r2]                 ; r14 -> input args
+        B       swi_vswi_common
+
 |_swix|
         ORR     r0, r0, #&20000
         TST     r1, #&FF0                 ; check for use of input regs. 4 to 9, or of block param
@@ -50,10 +69,9 @@ $name   MRS     lr, CPSR
 
         STMFD   sp!, {r2, r3}             ; put 1st two variadic args on stack
         STMDB   sp!, {r1, r4-r9, lr}      ; save stuff
-
+        ADD     r14, sp, #8*4             ; r14 -> input args
+swix_vswix_common
         SUB     sp, sp, #5*4              ; so we can use tail code common with dynamic version (and room for regs stash)
-
-        ADD     r14, sp, #(5+8)*4         ; r14 -> input args
         MOV     r12, r0                   ; target SWI code
         STR     fp, [sp]                  ; stash fp
 
@@ -78,10 +96,9 @@ swix_even_more_tedious
 |_swi|
         STMFD   sp!, {r2, r3}             ; put 1st two variadic args on stack
         STMDB   sp!, {r1, r4-r9, lr}      ; save stuff
-
+        ADD     r14, sp, #8*4             ; r14 -> input args
+swi_vswi_common
         SUB     sp, sp, #5*4              ; so we can use tail code common with dynamic version (and room for regs stash)
-
-        ADD     r14, sp, #(5+8)*4         ; r14 -> input args
         MOV     r12, r0                   ; target SWI code
         STR     fp, [sp]                  ; stash fp