1
;---------------------------------------------------------------------------------------------------------
2
; Source code converted by MSXBAS2ASM - MSX BASIC TO Z80 ASSEMBLY CONVERTER
3
; MSXBAS2ASM developed by Amaury Carvalho, 2019, Brazil
4
; http://launchpad.net/msxbas2asm
5
;---------------------------------------------------------------------------------------------------------
7
;--------------------------------------------------------
8
; MSX BIOS DATA/FUNCTION POINTERS
9
;--------------------------------------------------------
11
;---------------------------------------------------------------------------------------------------------
13
;---------------------------------------------------------------------------------------------------------
15
BIOS_CALBAS: equ 0x0159
16
BIOS_OUTDO: equ 0x0018 ; output to current device (i.e. screen)
17
BIOS_CHPUT: equ 0x00A2
19
BIOS_POSIT: equ 0x00C6
21
BIOS_CHGET: equ 0x009F
22
BIOS_CHSNS: equ 0x009C
23
BIOS_INLIN: equ 0x00B1
24
BIOS_PINLIN: equ 0x00AE
25
BIOS_QINLIN: equ 0x00B4
26
BIOS_GTSTCK: equ 0x00D5
27
BIOS_GTTRIG: equ 0x00D8
28
BIOS_GTPAD: equ 0x00DB
29
BIOS_GTPDL: equ 0x00DE
30
BIOS_DISSCR: equ 0x0041
31
BIOS_ENASCR: equ 0x0044
32
BIOS_CHGMOD: equ 0x005F
33
BIOS_CHGCLR: equ 0x0062
34
BIOS_CLRSPR: equ 0x0069
35
BIOS_INITXT: equ 0x006C ; init text mode 40 columns
36
BIOS_INIT32: equ 0x006F ; init text mode 32 columns
37
BIOS_INIGRP: equ 0x0072
38
BIOS_INIMLT: equ 0x0075
39
BIOS_SETTXT: equ 0x0078 ; set text mode 40 columns
40
BIOS_SETT32: equ 0x007B ; set text mode 32 columns
41
BIOS_SETGRP: equ 0x007E
42
BIOS_SETMLT: equ 0x0081
43
BIOS_CALPAT: equ 0x0084
44
BIOS_CALATR: equ 0x0087
45
BIOS_GSPSIZ: equ 0x008A
46
BIOS_GRPPRT: equ 0x008D
47
BIOS_ERAFNK: equ 0x00CC
48
BIOS_DSPFNK: equ 0x00CF
49
BIOS_TOTEXT: equ 0x00D2
50
BIOS_BREAKX: equ 0x00B7
51
BIOS_ISCNTC: equ 0x03FB
52
BIOS_CHKRAM: equ 0x0000
53
BIOS_GICINI: equ 0x0090
54
BIOS_WRTPSG: equ 0x0093
55
BIOS_REDPSG: equ 0x0096
56
BIOS_STRTMS: equ 0x0099
57
BIOS_KEYINT: equ 0x0038
58
BIOS_CALSLT: equ 0x001C
59
BIOS_ENASLT: equ 0x0024
60
BIOS_RSLREG: equ 0x0138
61
BIOS_SCALXY: equ 0x010E
62
BIOS_MAPXYC: equ 0x0111 ; in BC = X, DE = Y
63
BIOS_READC: equ 0x011D ; out A = color of current pixel
64
BIOS_SETATR: equ 0x011A ; in A = color code
65
BIOS_SETC: equ 0x0120 ; set current pixel to color from SETATR
66
BIOS_NSETCX: equ 0x0123 ; in HL = pixel fill count
67
BIOS_SCANR: equ 0x012C ; in B=Fill switch, DE=Skip count, out DE=Skip remainder, HL=Pixel count
68
BIOS_SCANL: equ 0x012F ; out HL=Pixel count
69
BIOS_FETCHC: equ 0x0114 ; out A = cursor mask, HL = VRAM address of cursor
70
BIOS_STOREC: equ 0x0117 ; in A = cursor mask, HL = VRAM address of cursor
71
BIOS_RESET: equ 0x7D17 ; restart BASIC
72
BIOS_IOALLOC: equ 0X7e6b ; memory setup
74
BIOS_GETVCP: equ 0x0150 ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
75
BIOS_GETVC2: equ 0x0153 ; get PSG voice buffer address (VOICEN = voice number, in L = byte number 0-36, out HL = address)
77
BIOS_CHPUT_LF: equ 0x0908
78
BIOS_CHPUT_CR: equ 0x0A81
79
BIOS_CHPUT_TAB: equ 0x0A71
82
BIOS_CHKNEW: equ 0x0165 ; C-flag set if screenmode = 5, 6, 7 or 8
83
BIOS_EXTROM: equ 0x015F
84
BIOS_SCALXY2: equ 0x008D ; in BC = X, DE = Y
85
BIOS_MAPXYC2: equ 0x0091 ; in BC = X, DE = Y
86
BIOS_SETC2: equ 0x009D ; set current pixel to color from SETATR
87
BIOS_READC2: equ 0x0095 ; out A = color of current pixel
88
BIOS_CHGMOD2: equ 0x00D1 ; in A = screenmode
89
BIOS_DOBOXF: equ 0x0079 ; hl = basic text pointer
90
BIOS_GRPPRT2: equ 0x0089 ; a = character
91
BIOS_CHGCLR2: equ 0x0111 ; change color, a = screen mode
92
BIOS_CALPAT2: equ 0x00F9
93
BIOS_CALATR2: equ 0x00FD
94
BIOS_GSPSIZ2: equ 0x0101
95
BIOS_CLRSPR2: equ 0x00F5
97
;---------------------------------------------------------------------------------------------------------
99
;---------------------------------------------------------------------------------------------------------
101
BIOS_VERSION: equ 0x002D ; 0 = MSX1, 1 = MSX2, 2 = MSX2+, 3 = MSXturboR
102
BIOS_FORCLR: equ 0xF3E9
103
BIOS_BAKCLR: equ 0xF3EA
104
BIOS_BDRCLR: equ 0xF3EB
105
BIOS_ATRBYT: equ 0xF3F2
106
BIOS_INTFLG: equ 0xFC9B
107
BIOS_EXPTBL: equ 0xFCC1
108
BIOS_JIFFY: equ 0xFC9E
109
BIOS_BOTTOM: equ 0xFC48
110
BIOS_HIMEM: equ 0xFC4A
111
BIOS_SCRMOD: equ 0xFCAF ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode.
112
BIOS_CLIKSW: equ 0xF3DB ; 0=keyboard click off, 1=keyboard click on
113
BIOS_GRPACX: equ 0xFCB7
114
BIOS_GRPACY: equ 0xFCB9
115
BIOS_DATLIN: equ 0xF6A3 ; 2 - line number of DATA statement read by READ statement
116
BIOS_DATPTR: equ 0xF6C8 ; 2 - address of data read by executing READ statement
117
BIOS_FLGINP: equ 0xF6A6 ; 1 - flag used in INPUT or READ
118
BIOS_TEMP: equ 0xF6A7 ; 2
119
BIOS_TEMP2: equ 0xF6BC ; 2
120
BIOS_TEMP3: equ 0xF69D ; 2
121
BIOS_TEMP8: equ 0xF69F ; 2
122
BIOS_TEMP9: equ 0xF7B8 ; 2
123
BIOS_OLDSCR: equ 0xFCB0 ; screen mode of the last text mode set
124
BIOS_LINL40: equ 0xF3AE ; width for 40 columns screen mode
125
BIOS_LINL32: equ 0xF3AF ; width for 32 columns screen mode
126
BIOS_LINLEN: equ 0xF3B0 ; current width for text screen mode
127
BIOS_CLMLST: equ 0xF3B2 ; minimum number of columns that must still be available on a line for a CRLF
128
BIOS_TXTNAM: equ 0xF3B3 ; characters table name
130
BIOS_VOICEN: equ 0xFB38 ; PSG voice number
131
BIOS_MCLTAB: equ 0xF956
132
BIOS_PRSCNT: equ 0xFB35
133
BIOS_SAVSP: equ 0xFB36
134
BIOS_QUEUEN: equ 0xFB3E
135
BIOS_MUSICF: equ 0xFB3F ;contains 3 bit flags set by the STRTMS. Bits 0, 1 and 2 correspond to VOICAQ, VOICBQ and VOICCQ.
136
BIOS_PLYCNT: equ 0xFB40
138
BIOS_DRWFLG: equ 0xFCBB
139
BIOS_MCLFLG: equ 0xF958
141
BIOS_SLTROM: equ 0xFCC1
142
BIOS_RAMAD0: equ 0xF341 ; Main-RAM Slot (00000h~03FFFh)
143
BIOS_RAMAD1: equ 0xF342 ; Main-RAM Slot (04000h~07FFFh)
144
BIOS_RAMAD2: equ 0xF343 ; Main-RAM Slot (08000h~0BFFFh)
145
BIOS_RAMAD3: equ 0xF344 ; Main-RAM Slot (0C000h~0FFFFh)
149
;--------------------------------------------------------
150
; MSX BASIC DATA/FUNCTION POINTERS
151
;--------------------------------------------------------
153
;---------------------------------------------------------------------------------------------------------
154
; MSX BASIC FUNCTIONS
155
;---------------------------------------------------------------------------------------------------------
157
BASIC_AUTO: equ 0x3973
158
BASIC_AND: equ 0x3A18
159
BASIC_ATTR: equ 0x39FE
160
BASIC_BASE: equ 0x39BE
161
BASIC_BSAVE: equ 0x39CC
162
BASIC_BLOAD: equ 0x39CA
163
BASIC_BEEP: equ 0x39AC
164
BASIC_CALL: equ 0x39C0
165
BASIC_CLOSE: equ 0x3994
166
BASIC_COPY: equ 0x39D8
167
BASIC_CONT: equ 0x395E
168
BASIC_CLEAR: equ 0x3950
169
BASIC_CLOAD: equ 0x3962
170
BASIC_CSAVE: equ 0x3960
171
BASIC_CSRLIN: equ 0x39FC
172
BASIC_CIRCLE: equ 0x39A4
173
BASIC_COLOR: equ 0x39A6
174
BASIC_CLS: equ 0x396A
175
BASIC_CMD: equ 0x39DA
176
BASIC_DELETE: equ 0x397C
177
BASIC_DATA: equ 0x3934
178
BASIC_DIM: equ 0x3938
179
BASIC_DEFSTR: equ 0x3982
180
BASIC_DEFINT: equ 0x3984
181
BASIC_DEFSNG: equ 0x3986
182
BASIC_DEFDBL: equ 0x3988
183
BASIC_DSKO: equ 0x39CE
184
BASIC_DEF: equ 0x395A
185
BASIC_DSKI: equ 0x3A00
186
BASIC_DRAW: equ 0x39A8
187
BASIC_ELSE: equ 0x396E
188
BASIC_END: equ 0x392E
189
BASIC_ERASE: equ 0x3976
190
BASIC_ERROR: equ 0x3978
191
BASIC_ERL: equ 0x39EE
192
BASIC_ERR: equ 0x39F0
193
BASIC_EQU: equ 0x3A1E
194
BASIC_FOR: equ 0x3920
195
BASIC_FIELD: equ 0x398E
196
BASIC_FILES: equ 0x39AA
198
BASIC_GOTO: equ 0x393E
199
BASIC_GOSUB: equ 0x3948
200
BASIC_GET: equ 0x3990
201
BASIC_INPUT: equ 0x3936
203
BASIC_INSTR: equ 0x39F6
204
BASIC_IMP: equ 0x3A20
205
BASIC_INKEY: equ 0x3A04
206
BASIC_IPL: equ 0x39D6
207
BASIC_KILL: equ 0x39D4
208
BASIC_KEY: equ 0x3964
209
BASIC_LPRINT: equ 0x394C
210
BASIC_LLIST: equ 0x3968
211
BASIC_LET: equ 0x393C
212
BASIC_LOCATE: equ 0x39DC
213
BASIC_LINE: equ 0x398A
214
BASIC_LOAD: equ 0x3996
215
BASIC_LSET: equ 0x399C
216
BASIC_LIST: equ 0x3952
217
BASIC_LFILES: equ 0x39A2
218
BASIC_MOTOR: equ 0x39C8
219
BASIC_MERGE: equ 0x3998
220
BASIC_MOD: equ 0x3A22
221
BASIC_MAX: equ 0x39C6
222
BASIC_NEXT: equ 0x3932
223
BASIC_NAME: equ 0x39D2
224
BASIC_NEW: equ 0x3954
225
BASIC_NOT: equ 0x39EC
226
BASIC_OPEN: equ 0x398C
227
BASIC_OUT: equ 0x3964
230
BASIC_OFF: equ 0x3A02
231
BASIC_PRINT: equ 0x394E
232
BASIC_PUT: equ 0x3992
233
BASIC_POKE: equ 0x395C
234
BASIC_PSET: equ 0x39B0
235
BASIC_PRESET: equ 0x39B2
236
BASIC_POINT: equ 0x3A06
237
BASIC_PAINT: equ 0x39AA
238
BASIC_PLAY: equ 0x39AE
239
BASIC_RETURN: equ 0x3948
240
BASIC_READ: equ 0x393A
241
BASIC_RUN: equ 0x3940
242
BASIC_RESTORE:equ 0x3944
243
BASIC_REM: equ 0x394A
244
BASIC_RESUME: equ 0x397A
245
BASIC_RSET: equ 0x399E
246
BASIC_RENUM: equ 0x3980
247
BASIC_SCREEN: equ 0x39B6
248
BASIC_SPRITE: equ 0x39BA
249
BASIC_STOP: equ 0x394C
250
BASIC_SWAP: equ 0x3974
251
BASIC_SET: equ 0x39D0
252
BASIC_SAVE: equ 0x39A0
253
BASIC_SPC: equ 0x39EA
254
BASIC_STEP: equ 0x39E4
255
BASIC_STRING: equ 0x39F2
256
BASIC_SPACE1: equ 0x397E
257
BASIC_SOUND: equ 0x39B4
258
BASIC_THEN: equ 0x39E0
259
BASIC_TRON: equ 0x3970
260
BASIC_TROFF: equ 0x3972
261
BASIC_TAB: equ 0x39E2
263
BASIC_TIME: equ 0x39C2
264
BASIC_USING: equ 0x39F4
265
BASIC_USR: equ 0x39E6
266
BASIC_VARPTR: equ 0x39FA
267
BASIC_VDP: equ 0x39BC
268
BASIC_VPOKE: equ 0x39B8
269
BASIC_WIDTH: equ 0x396C
270
BASIC_WAIT: equ 0x3958
271
BASIC_XOR: equ 0x3A1C
272
BASIC_ABS: equ 0x39E8
273
BASIC_ATN: equ 0x39F8
274
BASIC_ASC: equ 0x3A06
275
BASIC_BIN: equ 0x3A16
276
BASIC_CINT: equ 0x3A18
277
BASIC_CSNG: equ 0x3A1A
278
BASIC_CDBL: equ 0x3A1C
279
BASIC_CVI: equ 0x3A2C
280
BASIC_CVS: equ 0x3A2E
281
BASIC_CVD: equ 0x3A30
282
BASIC_COS: equ 0x39F4
283
BASIC_CHR: equ 0x3A08
284
BASIC_DSKF: equ 0x3A28
285
BASIC_EXP: equ 0x39F2
286
BASIC_EOF: equ 0x3A32
287
BASIC_FRE: equ 0x39FA
288
BASIC_FIX: equ 0x3A1E
289
BASIC_FPOS: equ 0x3A2A
290
BASIC_HEX: equ 0x3A12
291
BASIC_INT: equ 0x39E6
292
BASIC_INP: equ 0x39FC
293
BASIC_LPOS: equ 0x3A14
294
BASIC_LOG: equ 0x39F0
295
BASIC_LOC: equ 0x3A34
296
BASIC_LEN: equ 0x3A00
297
BASIC_LEFT: equ 0x39DE
298
BASIC_LOF: equ 0x3A36
299
BASIC_MKI: equ 0x3A38
300
BASIC_MKS: equ 0x3A3A
301
BASIC_MKD: equ 0x3A3C
302
BASIC_MID: equ 0x39E2
303
BASIC_OCT: equ 0x3A10
304
BASIC_POS: equ 0x39FE
305
BASIC_PEEK: equ 0x3A0A
306
BASIC_PDL: equ 0x3A24
307
BASIC_PAD: equ 0x3A26
308
BASIC_RIGHT: equ 0x39E0
309
BASIC_RND: equ 0x39EC
310
BASIC_SGN: equ 0x39E4
311
BASIC_SQR: equ 0x39EA
312
BASIC_SIN: equ 0x39EE
313
BASIC_STR: equ 0x3A02
314
BASIC_SPACE2: equ 0x3A0E
315
BASIC_STICK: equ 0x3A20
316
BASIC_STRIG: equ 0x3A22
317
BASIC_TAN: equ 0x39F6
318
BASIC_VAL: equ 0x3A04
319
BASIC_VPEEK: equ 0x3A0C
321
BASIC_TRAP_ENABLE: equ 0x631B ; ON INTERVAL/KEY/SPRITE/STOP/STRIG - hl = pointer to trap block
322
BASIC_TRAP_DISABLE: equ 0x632B ; hl = pointer to trap block
323
BASIC_TRAP_ACKNW: equ 0x6358 ; hl, acknowledge trap (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
324
BASIC_TRAP_PAUSE: equ 0x6331 ; hl
325
BASIC_TRAP_UNPAUSE: equ 0x633E ; hl
326
BASIC_TRAP_CLEAR: equ 0x636E
328
BASIC_PLAY_DIRECT: equ 0x744C
329
BASIC_DRAW_DIRECT: equ 0x568C
331
BASIC_READYR: equ 0x409B
332
BASIC_READYC: equ 0x7D17
333
BASIC_FACEVAL: equ 0x4DC7
335
BASIC_ERROR_HANDLER:equ 0x406F
336
BASIC_ERROR_SYNTAX: equ 0x4055
337
BASIC_ERROR_DIVZER: equ 0x4058
338
BASIC_ERROR_OVRFLW: equ 0x4067
339
BASIC_ERROR_ARRAY: equ 0x405E
340
BASIC_ERROR_TYPMIS: equ 0x406D
342
; BASIC ERROR CODES TO BASIC_ERROR_HANDLER
343
; 01 NEXT without FOR 19 Device I/O error
344
; 02 Syntax error 20 Verify error
345
; 03 RETURN without GOSUB 21 No RESUME
346
; 04 Out of DATA 22 RESUME without error
347
; 05 Illegal function call 23 Unprintable error
348
; 06 Overflow 24 Missing operand
349
; 07 Out of memory 25 Line buffer overflow
350
; 08 Undefined line number 50 FIELD overflow
351
; 09 Subscript out of range 51 Internal error
352
; 10 Redimensioned array 52 Bad file number
353
; 11 Division by zero 53 File not found
354
; 12 Illegal direct 54 File already open
355
; 13 Type mismatch 55 Input past end
356
; 14 Out of string space 56 Bad file name
357
; 15 String too long 57 Direct statement in file
358
; 16 String formula too complex 58 Sequential I/O only
359
; 17 Can't CONTINUE 59 File not OPEN
360
; 18 Undefined user function
362
;---------------------------------------------------------------------------------------------------------
363
; MSX BASIC WORK AREAS
364
;---------------------------------------------------------------------------------------------------------
366
BASIC_DAC: equ 0xF7F6 ; 16
367
BASIC_ARG: equ 0xF847 ; 16
368
BASIC_VALTYP: equ 0xF663
369
BASIC_RNDX: equ 0xF857
370
BASIC_BUF: equ 0xF55E ; 259
371
BASIC_KBUF: equ 0xF41F ; 318
372
BASIC_SWPTMP: equ 0xF7BC ; 8
373
BASIC_STRBUF: equ 0xF7C5 ; 43
374
BASIC_TXTTAB: equ 0xF676
375
BASIC_VARTAB: equ 0xF6C2
376
BASIC_ARYTAB: equ 0xF6C4
377
BASIC_STREND: equ 0xF6C6
378
BASIC_STKTOP: equ 0xF674
379
BASIC_FRETOP: equ 0xF69B
380
BASIC_MEMSIZ: equ 0xF672
382
BASIC_TEMPPT: equ 0xF678 ; 2 Starting address of unused area of temporary descriptor.
383
BASIC_TEMPST: equ 0xF67A ; 30 Temporary descriptors.
385
BASIC_DATPTR: equ 0xF6C8 ; 2 Pointer to next data to read from the instruction DATA. Modified by RESTORE.
386
BASIC_DATLIN: equ 0xF6A3 ; 2 Número de linha do comando DATA para o comando READ.
387
BASIC_DORES: equ 0xF664 ; 1 Usada pelo comando DATA para manter o texto no formato ASCII.
388
BASIC_DEFTBL: equ 0xF6CA ; 26 table of variables defined by DEFINT, DEFSTR, DEFSNG and DEFDBL for each alphabet letter (2 = integer, 3 = String, 4 = Simple precision, 8 = Double precision).
390
BASIC_CURLIN: equ 0xF41C ; BASIC current line number
391
BASIC_INTVAL: equ 0xFCA0 ; interval value
392
BASIC_INTCNT: equ 0xFCA2 ; interval current count
394
BASIC_PRMPRV: equ 0xF74C ; Pointer to previous parameter block in PARM1
396
BASIC_TRPTBL: equ 0xFC4C ; 78 trap table - array of 3 bytes - state[1] (bit 0=on, bit 1=stop, bit 2=active) + address[2]
398
BASIC_TRPTBL_KEY: equ 0xFC4C ; 30 ON KEY GOSUB
399
BASIC_TRPTBL_STOP: equ 0xFC6A ; 3 ON STOP GOSUB
400
BASIC_TRPTBL_SPRITE: equ 0xFC6D ; 3 ON SPRITE GOSUB
401
BASIC_TRPTBL_STRIG: equ 0xFC70 ; 15 ON STRIG GOSUB
402
BASIC_TRPTBL_INTERVAL: equ 0xFC7F ; 3 ON INTERVAL GOSUB
403
BASIC_TRPTBL_OTHER: equ 0xFC82 ; 24 reserved for expansion
405
BASIC_ONGSBF: equ 0xFBD8 ; 1 trap occurred counter (0=not occurred)
409
;--------------------------------------------------------
411
;--------------------------------------------------------
413
;--------------------------------------------------------
415
;--------------------------------------------------------
416
COMPILE_TO_ROM: EQU 1
418
MACRO __call_basic,CALL_PARM
423
if defined COMPILE_TO_DOS
425
MACRO __call_bios,CALL_PARM
426
;ld iy,(BIOS_EXPTBL-1)
428
call BIOS_CALBAS ; BIOS_CALSLT
433
MACRO __call_bios,CALL_PARM
440
push hl ; save parameter
444
pop iy ; restore PC of caller
445
pop hl ; get next parameter
446
push iy ; save PC of caller
450
pop iy ; restore PC of caller
451
push hl ; save return parameter
452
push iy ; save PC of caller
456
pop iy ; restore PC of caller
457
push hl ; save return parameter
458
push iy ; save PC of caller
462
MACRO set.line.number, line_number
463
ld bc, line_number ; current line number
464
ld (BASIC_CURLIN), bc
468
ld a, (BIOS_INTFLG) ; verify CTRL+BREAK
474
ld a, (BASIC_ONGSBF) ; trap occured counter
480
;---------------------------------------------------------------------------------------------------------
482
;---------------------------------------------------------------------------------------------------------
484
romSize: equ 0x8000 ; ROM size (32k)
485
pageSize: equ 0x4000 ; Page size (16k)
486
lowLimitSize: equ 0x400 ; 10% of a page size
488
if defined COMPILE_TO_BIN
490
pgmArea: equ 0x8000 ; page 2 - program area
491
ramArea: equ 0xc000 ; page 3 - free RAM start area
493
org pgmArea ; program binary type start address
494
db 0FEh ; binary file ID
495
dw start_pgm ; begin address
496
dw end_file - 1 ; end address
497
dw start_pgm ; program execution address (for ,R option)
500
if defined COMPILE_TO_ROM
502
pgmArea: equ 0x4000 ; page 1 and 2 - program area
503
ramArea: equ 0xc000 ; page 3 - free RAM start area
505
org pgmArea ; program rom type start address
506
db 'AB' ; rom file ID
508
dw 0x0000 ; STATEMENT
515
pgmArea: equ 0x8000 ; page 2 - program area
516
ramArea: equ 0xc000 ; page 3 - free RAM start area
518
org pgmArea ; program DOS type start address ; 0x0100
524
;---------------------------------------------------------------------------------------------------------
526
;---------------------------------------------------------------------------------------------------------
528
if defined COMPILE_TO_ROM
531
call PROGRAM_SLOT_2_RESTORE
532
__call_basic BASIC_READYR ; warm start Basic
537
call PROGRAM_SLOT_GET
538
ld (BIOS_RAMAD2), a ; Save RAM slot of page 8000h-BFFFh
541
PROGRAM_SLOT_2_RESTORE:
544
jp BIOS_ENASLT ; Select the RAM on page 8000h-BFFFh
546
PROGRAM_SLOT_2_ENABLE:
548
call PROGRAM_SLOT_ENABLE_SUB
550
jp BIOS_ENASLT ; Select the ROM on page 8000h-BFFFh
552
PROGRAM_SLOT_1_ENABLE:
556
call PROGRAM_SLOT_ENABLE_SUB
558
jp BIOS_ENASLT ; Select the ROM on page 4000h-7FFFh
560
PROGRAM_SLOT_ENABLE_SUB:
563
and 3 ;Keep bits corresponding to the page
582
; a <- slot ID formatted FxxxSSPP
583
; Modifies: af, bc, de, hl
584
; ref: https://www.msx.org/forum/msx-talk/development/fusion-c-and-htimi#comment-366469
588
jr z,PrimaryShiftContinue
593
PrimaryShiftContinue:
595
jr z,PrimaryShiftDone
610
inc hl ; move to SLTTBL
617
jr z,SecondaryShiftContinue
622
SecondaryShiftContinue:
624
jr nz,SecondaryShiftDone
634
if defined COMPILE_TO_DOS
639
__call_bios BIOS_ENASLT ; Select main ROM on page 0 (0000h~3FFFh)
645
__call_bios BIOS_ENASLT ; Select main ROM on page 1 (4000h~7FFFh)
652
;---------------------------------------------------------------------------------------------------------
654
;---------------------------------------------------------------------------------------------------------
656
start_pgm: ; start of the program
658
if defined COMPILE_TO_DOS
660
call BIOS_SLOT_ENABLE ; enable bios on page 0
661
call BASIC_SLOT_ENABLE ; enable basic on page 1
664
if defined COMPILE_TO_ROM
666
call PROGRAM_SLOT_2_SAVE ; save slot on page 2
667
call PROGRAM_SLOT_2_ENABLE ; enable program on page 2
672
__call_bios BIOS_ERAFNK ; turn off function keys display
673
__call_bios BIOS_GICINI ; initialize sound system
674
__call_bios BIOS_INITXT ; initialize text screen
676
ld (BIOS_CLIKSW), a ; disable keyboard click
678
ld (BASIC_CURLIN), bc ; interpreter in direct mode
679
__call_basic BASIC_TRAP_CLEAR ; clear traps work space
680
;call INITIALIZE_PARAMETERS ; initialize parameters stack
681
call memory.init ; initialize memory allocation
682
call INITIALIZE_VARIABLES ; initialize variables
686
call CLS ; action call
689
ld hl, LIT_5 ; parameter
691
call PRINT ; action call
692
ld hl, PRINT.CRLF ; parameter
694
call PRINT ; action call
697
ld hl, LIT_7 ; parameter
699
ld hl, IDF_6 ; parameter
701
call LET ; action call
704
ld hl, LIT_8 ; parameter
706
call PRINT ; action call
707
ld hl, IDF_6 ; parameter
709
call PRINT ; action call
710
ld hl, PRINT.CRLF ; parameter
712
call PRINT ; action call
715
ld hl, IDF_6 ; parameter
717
ld hl, LIT_10 ; parameter
719
call MATH.ADD ; action call
720
ld hl, IDF_6 ; parameter
722
call LET ; action call
725
ld hl, LIT_12 ; parameter
727
call PRINT ; action call
728
ld hl, IDF_6 ; parameter
730
call PRINT ; action call
731
ld hl, PRINT.CRLF ; parameter
733
call PRINT ; action call
736
ld hl, LIT_14 ; parameter
738
ld hl, LIT_15 ; parameter
740
call MATH.ADD ; action call
741
ld hl, LIT_16 ; parameter
743
call MATH.ADD ; action call
744
ld hl, IDF_13 ; parameter
746
call LET ; action call
749
ld hl, LIT_17 ; parameter
751
call PRINT ; action call
752
ld hl, IDF_13 ; parameter
754
call PRINT ; action call
755
ld hl, PRINT.CRLF ; parameter
757
call PRINT ; action call
760
ld hl, LIT_18 ; parameter
762
ld hl, LIT_19 ; parameter
764
call MATH.ADD ; action call
765
ld hl, LIT_20 ; parameter
767
ld hl, LIT_21 ; parameter
769
call MATH.MULT ; action call
770
call MATH.ADD ; action call
771
ld hl, IDF_6 ; parameter
773
call LET ; action call
776
ld hl, LIT_23 ; parameter
778
call PRINT ; action call
779
ld hl, IDF_6 ; parameter
781
call PRINT ; action call
782
ld hl, PRINT.CRLF ; parameter
784
call PRINT ; action call
787
ld hl, LIT_24 ; parameter
789
ld hl, LIT_25 ; parameter
791
call MATH.ADD ; action call
792
ld hl, LIT_26 ; parameter
794
ld hl, LIT_27 ; parameter
796
call MATH.MULT ; action call
797
call MATH.ADD ; action call
798
ld hl, LIT_28 ; parameter
800
call MATH.ADD ; action call
801
ld hl, LIT_29 ; parameter
803
call MATH.SUB ; action call
804
ld hl, IDF_13 ; parameter
806
call LET ; action call
809
ld hl, LIT_31 ; parameter
811
call PRINT ; action call
812
ld hl, IDF_13 ; parameter
814
call PRINT ; action call
815
ld hl, PRINT.CRLF ; parameter
817
call PRINT ; action call
820
ld hl, LIT_33 ; parameter
822
call MATH.NEG ; action call
823
ld hl, IDF_32 ; parameter
825
call LET ; action call
828
ld hl, LIT_35 ; parameter
830
call PRINT ; action call
831
ld hl, IDF_32 ; parameter
833
call PRINT ; action call
834
ld hl, PRINT.CRLF ; parameter
836
call PRINT ; action call
839
ld hl, IDF_32 ; parameter
841
ld hl, LIT_37 ; parameter
843
call MATH.ADD ; action call
844
ld hl, IDF_36 ; parameter
846
call LET ; action call
849
ld hl, LIT_38 ; parameter
851
call PRINT ; action call
852
ld hl, IDF_36 ; parameter
854
call PRINT ; action call
855
ld hl, PRINT.CRLF ; parameter
857
call PRINT ; action call
860
ld hl, LIT_40 ; parameter
862
ld hl, IDF_39 ; parameter
864
call LET ; action call
867
ld hl, LIT_42 ; parameter
869
ld hl, LIT_43 ; parameter
871
ld hl, LIT_44 ; parameter
873
call MATH.ADD ; action call
874
call MATH.MULT ; action call
875
ld hl, IDF_39 ; parameter
877
call MATH.DIV ; action call
878
ld hl, IDF_41 ; parameter
880
call LET ; action call
883
ld hl, LIT_46 ; parameter
885
call PRINT ; action call
886
ld hl, IDF_41 ; parameter
888
call PRINT ; action call
889
ld hl, PRINT.CRLF ; parameter
891
call PRINT ; action call
894
call PAUSE ; action call
899
;---------------------------------------------------------------------------------------------------------
901
;---------------------------------------------------------------------------------------------------------
903
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
905
ld (BIOS_CLIKSW), a ; enable keyboard click
907
if defined COMPILE_TO_ROM
910
__call_basic BASIC_READYR ; warm start Basic
913
ret ; end of the program
915
;__call_bios BIOS_GICINI ; initialize sound system
916
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
917
; __call_bios BIOS_RESET ; restart Basic
919
; __call_basic BASIC_END ; end to Basic
923
;---------------------------------------------------------------------------------------------------------
925
;---------------------------------------------------------------------------------------------------------
930
; out IX = variable assigned address
931
pop.parm ; get variable address parameter
932
push hl ; just to transfer hl to ix
934
ld a, (ix) ; get variable type
935
cp 3 ; test if string
936
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
937
ld a, (ix + 3) ; get variable string length
939
jr z, LET.PARM ; if zero, it isn't necessary to free memory
940
ld c, (ix + 4) ; get old string address low
941
ld b, (ix + 5) ; get old string address high
942
push ix ; save variable address
943
push bc ; just to transfer bc (old string address) to ix
945
call memory.free ; free memory
946
pop ix ; restore variable address
947
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
948
ld a, (ix + 2) ; get variable type flag
949
or a ; cp 0 - test type flag (0=any, 255=fixed)
950
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
951
LET.ANY: push ix ; just to transfer ix (variable address) to de
953
ldi ; copy 1 byte from hl (data address) to de (variable address)
954
inc de ; go to variable data area
956
inc hl ; go to data data area
958
ld bc, 8 ; data = 8 bytes
959
ldir ; copy bc bytes from hl (data address) to de (variable address)
960
ld a, (ix) ; get variable type
961
cp 3 ; test if string
962
ret nz ; if not string, return
963
jp LET.STRING ; else do string treatment (in ix = variable address)
964
LET.FIXED: push ix ; save variable destination address
965
push hl ; save variable source address
966
ld a, (ix) ; get variable fixed type, and hl has parameter data address
967
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
969
pop ix ; restore variable address
970
ld a, (ix) ; get variable destination type again
971
cp 3 ; test if string
972
jr nz, LET.VALUE ; if not string, do value treatment
973
ld a, (de) ; get variable source type again
974
cp 3 ; test if string
975
jr nz, LET.FIX1 ; if not string, get casted string size
980
ld (ix + 3), a ; source string size
983
call GET_STR.LENGTH ; get string length (in HL, out B)
985
ld (ix + 3), b ; set variable length
986
LET.FIX2: ld (ix + 4), l ; casted data address low
987
ld (ix + 5), h ; casted data address high
988
jp LET.STRING ; do string treatment (in ix = variable address)
989
LET.VALUE: push ix ; just to transfer ix (variable address) to de
991
inc de ; go to variable data area (and the data from its casted)
994
ld bc, 8 ; data = 8 bytes
995
ldir ; copy bc bytes from hl (data address) to de (variable address)
997
LET.STRING: ld a, (ix + 3) ; string size
998
or a ; cp 0 - test if null
999
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
1000
ld bc, LIT_NULL_STR ; else, set to a null string literal
1001
ld (ix + 4), c ; variable address low
1002
ld (ix + 5), b ; variable address high
1004
LET.ALLOC: push ix ; save variable address
1005
ld l, (ix + 4) ; source string address low
1006
ld h, (ix + 5) ; source string address high
1007
push hl ; save copy from address
1008
ld c, (ix + 3) ; get variable length
1010
inc bc ; string length have one more byte from zero terminator
1011
push bc ; save variable lenght + 1
1012
call memory.alloc ; in bc = size, out ix = address, nz=OK
1014
push ix ; just to transfer memory address from ix to de
1016
pop bc ; restore bytes to be copied
1017
pop hl ; restore copy from string address
1018
push de ; save copy to address
1019
ldir ; copy bc bytes from hl (data address) to de (variable address)
1022
pop de ; restore copy to address
1023
pop ix ; restore variable address
1024
ld (ix + 4), e ; put memory address low into variable
1025
ld (ix + 5), d ; put memory address high into variable
1026
ret ; variable assigned
1031
pop.parm ; get parameter boolean result in hl
1034
ld a, (ix+5) ; put boolean integer result in a
1040
if defined EXIST_DATA_SET
1042
jp z, gfxClearTileScreen
1044
xor a ; reset Z flag
1045
__call_bios BIOS_CLS ; clear screen
1051
pop.parm ; get first parameter
1054
ret z ; return if string size zero
1055
if defined EXIST_DATA_SET
1056
ld (BIOS_TEMP), a ; size of string
1060
; discard if first char < 32 or > 126
1067
; adjust default color
1071
sra a ; Y / 8 = bank
1080
call gfxSetTileDefaultColor
1087
ld hl, (BIOS_GRPACY)
1089
;call MATH.MULT.16 ; slow y * 32
1099
ld de, (BIOS_GRPACX)
1101
ld de, (BIOS_GRPNAM)
1116
call MATH.PARM.POP ; get parameters into DAC/ARG
1117
ld a, (BASIC_VALTYP) ;
1118
cp 2 ; test if integer
1119
jp z, MATH.ADD.INT ;
1120
cp 3 ; test if string
1121
jp z, STRING.CONCAT ;
1122
cp 4 ; test if single
1123
jp z, MATH.ADD.SGL ;
1124
jp MATH.ADD.DBL ; it is a double
1129
call MATH.PARM.POP ; get parameters into DAC/ARG
1130
ld a, (BASIC_VALTYP) ;
1131
cp 2 ; test if integer
1132
jp z, MATH.MULT.INT ;
1133
cp 3 ; test if string
1135
cp 4 ; test if single
1136
jp z, MATH.MULT.SGL ;
1137
jp MATH.MULT.DBL ; it is a double
1142
call MATH.PARM.POP ; get parameters into DAC/ARG
1143
ld a, (BASIC_VALTYP) ;
1144
cp 2 ; test if integer
1145
jp z, MATH.SUB.INT ;
1146
cp 3 ; test if string
1148
cp 4 ; test if single
1149
jp z, MATH.SUB.SGL ;
1150
jp MATH.SUB.DBL ; it is a double
1155
call CLEAR.DAC ; put zero in DAC
1156
pop.parm ; get parameter
1157
call COPY_TO.ARG ; put in ARG
1158
cp 2 ; test if integer
1159
jp z, MATH.SUB.INT ;
1160
cp 4 ; test if single
1161
jp z, MATH.SUB.SGL ;
1162
cp 8 ; test if double
1163
jp z, MATH.SUB.DBL ;
1169
call MATH.PARM.POP ; get parameters into DAC/ARG
1170
ld a, (BASIC_VALTYP) ;
1171
cp 2 ; test if integer
1172
jp z, MATH.DIV.INT ;
1173
cp 3 ; test if string
1175
cp 4 ; test if single
1176
jp z, MATH.DIV.SGL ;
1177
jp MATH.DIV.DBL ; it is a double
1188
__call_bios BIOS_BEEP
1189
__call_bios BIOS_CHGET
1200
;---------------------------------------------------------------------------------------------------------
1201
; MSX BASIC SUPPORT CODE
1202
;---------------------------------------------------------------------------------------------------------
1204
if defined ON_ERROR or defined ON_INTERVAL or defined ON_KEY_START or defined ON_SPRITE or defined ON_STOP or defined ON_STRIG_START or defined TRAP_ENABLED or defined TRAP_DISABLED or defined TRAP_PAUSE or defined TRAP_UNPAUSE
1208
RUN_TRAPS.1: push hl
1219
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1221
ld a, (hl) ; trap status
1222
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1223
ret nz ; return if false
1225
ld e, (hl) ; get trap address
1232
ret z ; return if address zero
1234
__call_basic BASIC_TRAP_ACKNW
1235
__call_basic BASIC_TRAP_PAUSE
1236
ld hl, TRAP_HANDLER.1
1237
ld a, (BASIC_ONGSBF) ; save traps execution
1240
ld (BASIC_ONGSBF), a ; disable traps execution
1241
push hl ; next return will be to trap handler
1242
push de ; indirect jump to trap address
1244
TRAP_HANDLER.1: pop af
1245
ld (BASIC_ONGSBF), a ; restore traps execution
1248
cp 1 ; trap enabled?
1250
__call_basic BASIC_TRAP_UNPAUSE
1253
; hl = trap block, de = trap handler
1255
ld (hl), a ; trap block status
1257
ld (hl), e ; trap block handler (pointer)
1264
if defined SET_PLAY_VOICE_1 or defined SET_PLAY_VOICE_2 or defined SET_PLAY_VOICE_3 or defined DO_PLAY or defined MUSIC_PLAY or defined MUSIC_NEXT or defined MUSIC_STOP
1267
ld (BIOS_TEMP), a ; save voice number
1271
ret nz ; return if not string
1274
ld (BIOS_TEMP2), a ; save string size
1275
push hl ; string address
1276
ld a, (BIOS_TEMP) ; restore voice number
1277
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1279
ld a, (BIOS_TEMP2) ; restore string size
1280
ld (hl), a ; string size
1282
ld (hl), e ; string address
1286
ld D,H ; voice stack
1301
ld hl, BIOS_TEMP ; voice count
1315
__call_basic BASIC_PLAY_DIRECT
1322
;---------------------------------------------------------------------------------------------------------
1323
; VARIABLES ROUTINES
1324
;---------------------------------------------------------------------------------------------------------
1326
; input hl = variable address
1327
; input bc = variable name
1328
; input d = variable type
1329
INIT_VAR: ld (hl), d ; variable type
1331
ld (hl), c ; variable name 1
1333
ld (hl), b ; variable name 2
1347
CLEAR.VAR.LOOP: inc hl
1348
ld (hl), 0 ; data address/value
1351
; input HL = variable address
1352
; input A = variable output type
1353
; output HL = casted data address
1363
; input HL = variable address
1364
; output HL = variable address
1365
CAST_TO.INT: ;push af
1370
jp z, CAST_STR_TO.INT
1372
jp z, CAST_SGL_TO.INT
1374
jp z, CAST_DBL_TO.INT
1377
; input HL = variable address
1378
; output HL = variable address
1379
CAST_TO.STR: ;push af
1382
jp z, CAST_INT_TO.STR
1386
jp z, CAST_SGL_TO.STR
1388
jp z, CAST_DBL_TO.STR
1391
; input HL = variable address
1392
; output HL = variable address
1393
CAST_TO.SGL: ;push af
1396
jp z, CAST_INT_TO.SGL
1398
jp z, CAST_STR_TO.SGL
1402
jp z, CAST_DBL_TO.SGL
1405
; input HL = variable address
1406
; output HL = variable address
1407
CAST_TO.DBL: ;push af
1410
jp z, CAST_INT_TO.DBL
1412
jp z, CAST_STR_TO.DBL
1414
jp z, CAST_SGL_TO.DBL
1419
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1420
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1421
CAST_INT_TO.STR: call COPY_TO.DAC
1423
__call_bios MATH_FOUT ; convert DAC to string
1426
CAST_INT_TO.SGL: call COPY_TO.DAC
1427
__call_bios MATH_FRCSGL
1430
CAST_INT_TO.DBL: call COPY_TO.DAC
1431
__call_bios MATH_FRCDBL
1434
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1435
CAST_DBL_TO.INT: call COPY_TO.DAC
1436
__call_bios MATH_FRCINT
1439
CAST_STR_TO.INT: call CAST_STR_TO.VAL ;
1440
__call_bios MATH_FRCINT ;
1443
CAST_STR_TO.SGL: call CAST_STR_TO.VAL ;
1444
__call_bios MATH_FRCSGL ;
1447
CAST_STR_TO.DBL: call CAST_STR_TO.VAL ;
1448
__call_bios MATH_FRCDBL ;
1451
CAST_STR_TO.VAL: call GET_STR.ADDR ;
1453
__call_bios MATH_FIN ; convert string to a value type
1456
GET_INT.VALUE: inc hl ; output BC with integer value
1462
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1463
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1464
GET_INT.ADDR: ; same as GET_DBL.ADDR
1465
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1466
GET_DBL.ADDR: inc hl
1471
GET_STR.ADDR: push hl
1477
; input hl = string address
1478
; output b = string length
1479
GET_STR.LENGTH: ld b, 0
1480
GET_STR.LEN.NEXT: ld a, (hl)
1487
jr z, GET_STR.LEN.ERR
1489
GET_STR.LEN.ERR: ld b, 0
1491
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
1492
ld iy, (BASIC_ARG+1) ; string 2
1493
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
1494
cp (iy) ; char s1 = char s2?
1495
jr nz, STRING.COMPARE.NE ; if not equal...
1497
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
1498
ld a, (iy) ; next char from string 2
1500
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
1503
jr STRING.COMPARE.NX ; get next char pair
1504
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
1506
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
1507
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
1508
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
1509
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
1511
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
1513
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
1515
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
1516
ld a, (BASIC_ARG) ; s2 size
1517
add a, (ix) ; s3 size = s1 size + s2 size
1521
inc bc ; add 1 byte to size
1522
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1523
jp z, memory.error ;
1527
ld a, (BASIC_DAC) ; s1 size
1528
ld hl, (BASIC_DAC + 1) ; string 1
1529
call COPY_TO.STR ; copy to new memory
1530
ld a, (BASIC_ARG) ; s2 size
1531
ld hl, (BASIC_ARG + 1) ; string 2
1532
call COPY_TO.STR ; copy to new memory
1534
ld (de), a ; null terminated
1537
call COPY_TO.VAR_DUMMY.STR ;
1538
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
1539
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
1541
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
1543
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
1544
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
1545
or a ; cp 0 - is it the string end?
1547
__call_bios BIOS_CHPUT ; put the char (a) into text screen
1549
jr STRING.PRINT.T ; repeat
1550
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
1551
or a ; cp 0 - is it the string end?
1553
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
1555
jr STRING.PRINT.G1 ; repeat
1556
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
1557
or a ; cp 0 - is it the string end?
1559
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
1562
jr STRING.PRINT.G2 ; repeat
1564
; a = string size to copy
1565
; input hl = string from
1566
; input de = string to
1568
ret z ; avoid copy if size = zero
1570
ld c, a ; string size
1571
ldir ; copy bc bytes from hl to de
1573
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
1574
ld a, (LIT_QUOTE_CHAR)
1577
COPY_BAS_BUF.LOOP: ld a, (hl)
1579
jr z, COPY_BAS_BUF.EXIT
1583
jr COPY_BAS_BUF.LOOP
1584
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
1591
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
1593
jr nz, COPY_TO.VAR_DUMMY.DBL
1595
call GET_STR.LENGTH ; get string length
1597
ld a, b ; string length
1598
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
1599
ld (ix), 3 ; data type string
1601
ld (ix+2), 255 ; var type fixed
1602
ld (ix+3), a ; string length
1603
ld (ix+4), l ; data address low
1604
ld (ix+5), h ; data address high
1605
;call GET_STR.LENGTH ; get string length
1606
;ld (ix+3), b ; string length
1607
push ix ; output var address...
1610
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
1611
ld (ix), 2 ; data type string
1622
push ix ; output var address...
1625
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
1626
ld (ix), a ; data type
1631
push ix ; just to copy ix to de
1636
ldir ; copy bc bytes from hl (data address) to de (variable address)
1637
push ix ; output var address...
1640
GET_VAR_DUMMY.ADDR: push af ;
1643
ld ix, (VAR_DUMMY.POINTER) ;
1644
ld a, (VAR_DUMMY.COUNTER) ;
1645
GET_VAR_DUMMY.NEXT: add ix, de ;
1648
jr nz, GET_VAR_DUMMY.EXIT ;
1650
ld ix, VAR_DUMMY.DATA ;
1651
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
1652
ld (VAR_DUMMY.COUNTER), a ;
1653
ld a, (ix) ; get last var dummy type
1654
cp 3 ; is it string?
1655
call z, GET_VAR_DUMMY.FREE ; free string memory
1662
ld l, (ix+4) ; get string data address
1666
call memory.free ; free memory
1670
; input hl = variable address
1671
COPY_TO.DAC: ld de, BASIC_DAC
1672
COPY_TO.DAC.DATA: ld a, (hl)
1673
ld (BASIC_VALTYP), a
1677
ld bc, 8 ; data = 8 bytes
1678
ldir ; copy bc bytes from hl (data address) to de (variable address)
1680
COPY_TO.ARG: ld de, BASIC_ARG ;
1681
jr COPY_TO.DAC.DATA ;
1682
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
1684
ld bc, 8 ; data = 8 bytes
1685
ldir ; copy bc bytes from hl (data address) to de (variable address)
1687
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
1689
ld bc, 8 ; data = 8 bytes
1690
ldir ; copy bc bytes from hl (data address) to de (variable address)
1692
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
1693
ld de, BASIC_SWPTMP ;
1694
ld bc, 8 ; data = 8 bytes
1695
ldir ; copy bc bytes from hl (data address) to de (variable address)
1697
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
1699
ld bc, 8 ; data = 8 bytes
1700
ldir ; copy bc bytes from hl (data address) to de (variable address)
1703
exx ; save registers
1706
ld de, BASIC_SWPTMP ;
1707
ldir ; copy bc bytes from hl to de
1711
ldir ; copy bc bytes from hl to de
1713
ld hl, BASIC_SWPTMP ;
1715
ldir ; copy bc bytes from hl to de
1716
exx ; restore registers
1719
CLEAR.DAC: ld de, BASIC_DAC
1720
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
1723
ld bc, 8 ; data = 8 bytes
1724
ldir ; copy bc bytes from hl (data address) to de (variable address)
1726
CLEAR.ARG: ld de, BASIC_ARG
1731
;---------------------------------------------------------------------------------------------------------
1732
; MATH 16 BITS ROUTINES
1733
;---------------------------------------------------------------------------------------------------------
1735
MATH.PARM.POP: pop af ; get PC from caller stack
1736
ex af, af' ; save PC to temp
1737
pop.parm ; get first parameter
1738
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1739
pop.parm ; get second parameter
1740
ex af, af' ; restore PC from temp
1741
push af ; put again PC from caller in stack
1742
ex af, af' ; restore 1st data type
1743
push af ; save 1st data type
1744
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1745
pop bc ; restore 1st data type (ARG) in B
1746
cp b ; test if data type in A (DAC) = data type in B (ARG)
1747
ret z ; return if is equal data types
1748
MATH.PARM.CAST: push bc ; else cast both to double
1749
and 12 ; test if single/double
1750
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
1751
__call_bios MATH_FRCDBL ; convert DAC to double
1752
MATH.PARM.CST1: pop af ;
1753
and 12 ; test if single/double
1754
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
1755
ld (BASIC_VALTYP), a ;
1756
call COPY_TO.DAC_TMP ;
1757
call COPY_TO.ARG_DAC ;
1758
__call_bios MATH_FRCDBL ; convert ARG to double
1759
call COPY_TO.DAC_ARG ;
1760
call COPY_TO.TMP_DAC ;
1761
MATH.PARM.CST2: ld a, 8 ;
1762
ld (BASIC_VALTYP), a ;
1764
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
1765
pop af ; get PC from caller stack
1766
ex af, af' ; save PC to temp
1767
pop.parm ; get first parameter
1768
ld a, (hl) ; get parameter type
1769
and 2 ; test if integer
1770
jr z, MATH.PARM.POP.I1 ; do cast if not integer
1771
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1772
jr MATH.PARM.POP.I2 ; go to next parameter
1773
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
1774
__call_bios MATH_FRCINT ; convert DAC to int
1775
call COPY_TO.DAC_ARG ; copy DAC to ARG
1776
MATH.PARM.POP.I2: pop.parm ; get second parameter
1777
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1778
and 2 ; test if integer
1779
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
1780
__call_bios MATH_FRCINT ; convert DAC to int
1782
ld (BASIC_VALTYP), a ;
1784
ex af, af' ; restore PC from temp
1785
push af ; put again PC from caller in stack
1787
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
1793
; output in parm stack
1794
; http://www.z80.info/zip/zaks_book.pdf - page 104
1795
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
1796
ld bc, (BASIC_ARG+2) ;
1798
ld (BASIC_DAC+2), hl ;
1803
if defined MATH.SUB or defined MATH.NEG
1806
; output in parm stack
1807
; http://www.z80.info/zip/zaks_book.pdf - page 104
1808
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
1809
ld de, (BASIC_ARG+2) ;
1812
ld (BASIC_DAC+2), hl ;
1817
if defined MATH.MULT
1820
; output in parm stack
1821
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
1822
ld bc, (BASIC_ARG+2) ;
1824
ld (BASIC_DAC+2), hl ;
1827
; input HL = multiplicand
1828
; input BC = multiplier
1829
; output HL = result
1830
; http://www.z80.info/zip/zaks_book.pdf - page 131
1831
MATH.MULT.16: ld a, c ; low multiplier
1832
ld c, b ; high multiplier
1834
ld d, h ; multiplicand
1837
MULT16LOOP: srl c ; right shift multiplier high
1838
rra ; rotate right multiplier low
1839
jr nc, MULT16NOADD ; test carry
1840
add hl, de ; add multiplicand to result
1841
MULT16NOADD: ex de, hl
1842
add hl, hl ; double - shift multiplicand
1849
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
1851
; input AC = dividend
1852
; input DE = divisor
1853
; output AC = quotient
1854
; output HL = remainder
1855
; http://www.z80.info/zip/zaks_book.pdf - page 140
1856
MATH.DIV.16: ld hl, 0 ; clear accumulator
1857
ld b, 16 ; set counter
1858
DIV16LOOP: rl c ; rotate accumulator result left
1860
adc hl, hl ; left shift
1861
sbc hl, de ; trial subtract divisor
1862
jr nc, $ + 3 ; subtract was OK ($ = current location)
1863
add hl, de ; restore accumulator
1864
ccf ; calculate result bit
1865
djnz DIV16LOOP ; counter not zero
1866
rl c ; shift in last result bit
1872
if defined GFX_FAST or defined LINE
1874
; compare two signed 16 bits integers
1875
; HL < DE: Carry flag
1876
; HL = DE: Zero flag
1877
; http://www.z80.info/zip/zaks_book.pdf - page 531
1878
MATH.COMP.S16: ld a, h ; test high order byte
1879
and 0x80 ; test sign, clear carry
1880
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
1882
ret nz ; de is negative (and hl is positive)
1884
cp d ; signs are both positive, so normal compare
1886
ld a, l ; test low order byte
1889
MATH.COMP.S16.NEGM1:
1891
rla ; sign bit into carry
1892
ret c ; signs different
1894
cp d ; both signs negative
1904
MATH.ADD.SGL: ld a, 8 ;
1905
ld (BASIC_VALTYP), a ;
1906
MATH.ADD.DBL: __call_bios MATH_DECADD ;
1911
if defined MATH.SUB or defined MATH.NEG
1913
MATH.SUB.SGL: ld a, 8 ;
1914
ld (BASIC_VALTYP), a ;
1915
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
1920
if defined MATH.MULT
1922
MATH.MULT.SGL: ld a, 8 ;
1923
ld (BASIC_VALTYP), a ;
1924
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
1932
; output in parm stack
1933
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
1936
ld (BASIC_VALTYP), a ;
1937
__call_bios MATH_FRCDBL ; convert ARG to double
1939
MATH.DIV.SGL: ld a, 8 ;
1940
ld (BASIC_VALTYP), a ;
1941
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
1946
if defined MATH.IDIV
1949
; output in parm stack
1950
MATH.IDIV.SGL: ld a, 8 ;
1951
ld (BASIC_VALTYP), a ;
1952
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1955
ld (BASIC_VALTYP), a ;
1956
__call_bios MATH_FRCINT ; convert ARG to integer
1958
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
1961
ld de, (BASIC_ARG+2) ;
1965
ld (BASIC_DAC+2), hl ; quotient
1972
MATH.POW.INT: ld (BASIC_VALTYP), a ;
1973
__call_bios MATH_FRCDBL ; convert DAC to double
1976
ld (BASIC_VALTYP), a ;
1977
__call_bios MATH_FRCDBL ; convert ARG to double
1979
MATH.POW.SGL: ld a, 8 ;
1980
ld (BASIC_VALTYP), a ;
1981
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
1988
;MATH.MOD.SGL: ld a, 8 ;
1989
; ld (BASIC_VALTYP), a ;
1990
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1991
; call SWAP.DAC.ARG ;
1993
; ld (BASIC_VALTYP), a ;
1994
; __call_bios MATH_FRCINT ; convert ARG to integer
1995
; call SWAP.DAC.ARG ;
1996
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
1999
ld de, (BASIC_ARG+2) ;
2001
ld (BASIC_DAC+2), hl ; remainder
2008
; fast 16-bit integer square root
2009
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
2010
; 92 bytes, 344-379 cycles (average 362)
2011
; v2 - 3 t-state optimization spotted by Russ McNulty
2012
; call with hl = number to square root
2013
; returns a = square root
2090
if defined RANDOMIZE or defined SEED
2092
MATH.RANDOMIZE: di ;
2093
ld bc, (BIOS_JIFFY) ;
2096
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
2097
push bc ; in bc = new integer seed
2101
ld (BASIC_DAC+2), bc ; copy bc to dac
2102
ld a, 2 ; type integer
2103
ld (BASIC_VALTYP), a ;
2104
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
2105
__call_bios MATH_NEG ; DAC = -DAC
2106
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
2111
MATH.ERROR: ld e, 13 ; type mismatch
2112
__call_basic BASIC_ERROR_HANDLER ;
2116
;---------------------------------------------------------------------------------------------------------
2118
;---------------------------------------------------------------------------------------------------------
2120
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
2122
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
2124
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
2125
ld de, (BASIC_ARG+2) ;
2126
__call_bios MATH_ICOMP ;
2128
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
2129
ld de, (BASIC_ARG+2) ;
2130
__call_bios MATH_DCOMP ;
2132
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
2134
BOOLEAN.CMP.STR: call STRING.COMPARE ;
2137
if defined BOOLEAN.GT
2139
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
2141
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
2143
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
2145
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
2147
BOOLEAN.GT.RET: cp 0x01 ;
2148
jp z, BOOLEAN.RET.TRUE ;
2149
jp BOOLEAN.RET.FALSE ;
2152
if defined BOOLEAN.LT
2154
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
2156
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
2158
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
2160
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
2162
BOOLEAN.LT.RET: cp 0xFF ;
2163
jp z, BOOLEAN.RET.TRUE ;
2164
jp BOOLEAN.RET.FALSE ;
2168
if defined BOOLEAN.GE
2170
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
2172
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
2174
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
2176
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
2178
BOOLEAN.GE.RET: cp 0x01 ;
2179
jp z, BOOLEAN.RET.TRUE ;
2181
jp z, BOOLEAN.RET.TRUE ;
2182
jp BOOLEAN.RET.FALSE ;
2186
if defined BOOLEAN.LE
2188
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
2190
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
2192
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
2194
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
2196
BOOLEAN.LE.RET: cp 0xFF ;
2197
jp z, BOOLEAN.RET.TRUE ;
2199
jp z, BOOLEAN.RET.TRUE ;
2200
jp BOOLEAN.RET.FALSE ;
2204
if defined BOOLEAN.NE
2206
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
2208
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
2210
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
2212
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
2214
BOOLEAN.NE.RET: or a ; cp 0
2215
jp nz, BOOLEAN.RET.TRUE ;
2216
jp BOOLEAN.RET.FALSE ;
2220
if defined BOOLEAN.EQ
2222
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
2224
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
2226
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
2228
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2230
BOOLEAN.EQ.RET: or a ; cp 0
2231
jp z, BOOLEAN.RET.TRUE ;
2232
jp BOOLEAN.RET.FALSE ;
2236
if defined BOOLEAN.AND
2238
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2239
ld hl, BASIC_ARG+2 ;
2241
ld (BASIC_DAC+2), a ;
2243
ld a, (BASIC_DAC+3) ;
2245
ld (BASIC_DAC+3), a ;
2251
if defined BOOLEAN.OR
2253
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2254
ld hl, BASIC_ARG+2 ;
2256
ld (BASIC_DAC+2), a ;
2258
ld a, (BASIC_DAC+3) ;
2260
ld (BASIC_DAC+3), a ;
2266
if defined BOOLEAN.XOR
2268
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2269
ld hl, BASIC_ARG+2 ;
2271
ld (BASIC_DAC+2), a ;
2273
ld a, (BASIC_DAC+3) ;
2275
ld (BASIC_DAC+3), a ;
2281
if defined BOOLEAN.EQV
2283
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2284
ld hl, BASIC_ARG+2 ;
2287
ld (BASIC_DAC+2), a ;
2289
ld a, (BASIC_DAC+3) ;
2292
ld (BASIC_DAC+3), a ;
2298
if defined BOOLEAN.IMP
2300
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2301
ld hl, BASIC_ARG+2 ;
2304
ld (BASIC_DAC+2), a ;
2306
ld a, (BASIC_DAC+3) ;
2309
ld (BASIC_DAC+3), a ;
2315
if defined BOOLEAN.SHR
2317
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2318
ld a, (BASIC_ARG+2) ;
2320
jp z, MATH.PARM.PUSH ; return if not shift
2321
ld b, a ; shift count
2322
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2325
djnz BOOLEAN.SHR.INT.N ; next shift
2327
jp MATH.PARM.PUSH ; return DAC
2331
if defined BOOLEAN.SHL
2333
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2334
ld a, (BASIC_ARG+2) ;
2336
jp z, MATH.PARM.PUSH ; return if not shift
2337
ld b, a ; shift count
2338
BOOLEAN.SHL.INT.N: rl (ix) ;
2341
djnz BOOLEAN.SHL.INT.N ; next shift
2343
jp MATH.PARM.PUSH ; return DAC
2347
if defined BOOLEAN.NOT
2349
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2351
ld (BASIC_DAC+2), a ;
2352
ld a, (BASIC_DAC+3) ;
2354
ld (BASIC_DAC+3), a ;
2362
;---------------------------------------------------------------------------------------------------------
2363
; MEMORY ALLOCATION ROUTINES
2364
;---------------------------------------------------------------------------------------------------------
2365
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2366
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2367
; https://www.msx.org/users/samsaga2
2368
;---------------------------------------------------------------------------------------------------------
2369
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2370
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2371
block.next: equ 0 ; next free block address
2372
block.size: equ 2 ; size of block including header
2373
block: equ 4 ; block.next + block.size
2377
ld ix,memory.heap_start ; first block
2378
ld hl,memory.heap_start+block ; second block
2379
;; first block NEXT=secondblock, SIZE=0
2380
;; with this block we have a fixed start location
2381
;; because never will be allocated
2382
ld (ix+block.next),l
2383
ld (ix+block.next+1),h
2384
ld (ix+block.size),0
2385
ld (ix+block.size+1),0
2386
;; second block NEXT=0, SIZE=all
2387
;; the first and only free block have all available memory
2388
ld (ix+block.next+block),0
2389
ld (ix+block.next+block+1),0
2391
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2394
ld de, memory.heap_start + (block * 2) + 100
2396
;ld de, block * 2 + 100
2398
ld (ix+block.size+block),l
2399
ld (ix+block.size+block+1),h
2403
;; IN BC=size, OUT IX=memptr, NZ=ok
2411
ld ix,memory.heap_start ; this
2414
ld l,(ix+block.size)
2415
ld h,(ix+block.size+1)
2418
jp z, memory.alloc.exactfit
2419
jp c, memory.alloc.nextblock
2420
;; split found block
2421
memory.alloc.splitfit:
2422
;; free space must allow at least two blocks headers (current + next)
2424
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2427
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2428
memory.alloc.splitfit.do:
2429
;; newfreeblock = this + BC
2433
;; prevblock->next = newfreeblock
2434
ld (iy+block.next),l
2435
ld (iy+block.next+1),h
2436
;; newfreeblock->next = this->next
2438
pop iy ; iy = newfreeblock
2439
ld l,(ix+block.next)
2440
ld h,(ix+block.next+1)
2441
ld (iy+block.next),l
2442
ld (iy+block.next+1),h
2443
;; newfreeblock->size = this->size - BC
2444
ld l,(ix+block.size)
2445
ld h,(ix+block.size+1)
2448
ld (iy+block.size),l
2449
ld (iy+block.size+1),h
2451
ld (ix+block.size),c
2452
ld (ix+block.size+1),b
2454
;; use whole found block
2455
memory.alloc.exactfit:
2456
;; prevblock->next = this->next - remove block from free list
2457
ld l,(ix+block.next)
2458
ld h,(ix+block.next+1)
2459
ld (iy+block.next),l
2460
ld (iy+block.next+1),h
2469
memory.alloc.nextblock:
2470
ld l,(ix+block.next)
2471
ld h,(ix+block.next+1)
2478
;; this = this->next
2481
jp memory.alloc.find
2486
;; HL = IX - block_header_size
2493
ld ix,memory.heap_start
2495
ld e,(ix+block.next)
2496
ld d,(ix+block.next+1)
2499
jp z, memory.free.passedend
2500
sbc hl,de ; test this (HL) against next (DE)
2501
jr c, memory.free.found ; if DE > HL
2502
add hl,de ; restore hl value
2504
pop ix ; current = next
2507
;; ix=prev, hl=this, de=next
2509
add hl,de ; restore hl value
2510
ld (ix+block.next), l
2511
ld (ix+block.next+1), h ; prev->next = this
2514
ld (iy+block.next), e
2515
ld (iy+block.next+1), d ; this->next = next
2516
push ix ; prev x this
2521
call memory.free.coalesce
2522
pop ix ; this x next
2523
jr memory.free.coalesce
2527
memory.free.coalesce:
2528
ld c, (iy+block.size)
2529
ld b, (iy+block.size+1) ; bc = this->size
2533
adc hl, bc ; hl = this + this->size
2537
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
2538
jr z, memory.free.coalesce.do
2539
push ix ; else, new *this = *next
2542
memory.free.coalesce.do:
2543
ld l, (ix+block.size)
2544
ld h, (ix+block.size+1) ; hl = next->size
2546
adc hl, bc ; hl += this->size
2547
ld (iy+block.size), l
2548
ld (iy+block.size+1), h ; this->size = hl
2549
ld l, (ix+block.next)
2550
ld h, (ix+block.next+1) ; hl = next->next
2551
ld (iy+block.next), l
2552
ld (iy+block.next+1), h ; this->next = hl
2555
memory.free.passedend:
2556
;; append block at the end of the free list
2557
ld (ix+block.next),l
2558
ld (ix+block.next+1),h
2561
ld (iy+block.next),0
2562
ld (iy+block.next+1),0
2568
ld ix,memory.heap_start
2570
memory.get_free.count:
2572
add a,(ix+block.size)
2575
adc a,(ix+block.size+1)
2577
ld l,(ix+block.next)
2578
ld h,(ix+block.next+1)
2584
jr memory.get_free.count
2586
memory.error: ld e, 7 ; out of memory
2587
__call_basic BASIC_ERROR_HANDLER ;
2592
;---------------------------------------------------------------------------------------------------------
2594
;---------------------------------------------------------------------------------------------------------
2603
RET_MATH_LIB: call COPY_TO.TMP_DAC
2609
MATH_DECADD: ld ix, addSingle
2614
if defined MATH.SUB or defined MATH.NEG
2616
MATH_DECSUB: ld ix, subSingle
2621
if defined MATH.MULT
2623
MATH_DECMUL: ld ix, mulSingle
2630
MATH_DECDIV: ld ix, divSingle
2638
MATH_SNGEXP: ld ix, powSingle
2645
MATH_COS: ld ix, cosSingle
2652
MATH_SIN: ld ix, sinSingle
2659
MATH_TAN: ld ix, tanSingle
2666
MATH_ATN: ld ix, atanSingle
2673
MATH_SQR: ld ix, sqrtSingle
2680
MATH_LOG: ld ix, lnSingle
2687
MATH_EXP: ld ix, expSingle
2694
MATH_ABSFN: ld ix, absSingle
2699
if defined MATH.SEED or defined MATH.NEG
2701
MATH_NEG: ld ix, negSingle
2708
MATH_SGN: ld ix, sgnSingle
2713
if defined RND or defined MATH.SEED
2715
MATH_RND: ld ix, randSingle
2720
MATH_FRCINT: ld hl, BASIC_DAC
2733
ld (BASIC_VALTYP), a
2736
MATH_FRCDBL: ; same as MATH_FRCSGL
2737
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
2738
ld bc, BASIC_DAC ; output address
2741
ld (BASIC_VALTYP), a
2744
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
2746
jr nz, MATH_ICOMP.NE.HIGH
2749
jr nz, MATH_ICOMP.NE.LOW
2751
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
2753
jr nz, MATH_DCOMP.GT
2755
MATH_ICOMP.GT.HIGH: bit 7, d
2758
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
2761
MATH_XDCOMP: ; same as MATH_DCOMP
2762
MATH_DCOMP: ld ix, cmpSingle
2766
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
2768
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
2770
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
2773
if defined CAST_STR_TO.VAL
2775
MATH_FIN: ; HL has the source string
2776
ld a, (BASIC_VALTYP)
2777
cp 2 ; test if integer
2779
ld hl, (BASIC_DAC+2)
2784
MATH_FIN.1: ld BC, BASIC_DAC
2790
if defined CAST_INT_TO.STR
2792
MATH_FOUT: ld a, (BASIC_VALTYP)
2793
cp 2 ; test if integer
2795
ld hl, (BASIC_DAC+2)
2800
MATH_FOUT.1: ld hl, BASIC_DAC
2811
;---------------------------------------------------------------------------------------------------------
2813
; Copyright 2018 Zeda A.K. Thomas
2814
;---------------------------------------------------------------------------------------------------------
2816
; https://github.com/Zeda/z80float
2817
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
2818
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
2819
;---------------------------------------------------------------------------------------------------------
2821
; HL points to the first operand
2822
; DE points to the second operand (if needed)
2823
; IX points to the third operand (if needed, rare)
2824
; BC points to where the result should be output
2825
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
2826
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
2827
; exponent biased by +128.
2828
;---------------------------------------------------------------------------------------------------------
2829
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
2830
;---------------------------------------------------------------------------------------------------------
2832
;---------------------------------------------------------------------------------------------------------
2834
;---------------------------------------------------------------------------------------------------------
2836
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
2837
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
2838
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
2839
scrap: equ BASIC_HOLD8
2840
seed0: equ BASIC_RNDX
2841
seed1: equ seed0 + 4
2842
var48: equ scrap + 4
2845
addend2: equ scrap+7 ;4 bytes
2846
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
2847
var_y: equ var_x + 4 ;4 bytes
2848
var_z: equ var_y + 4 ;4 bytes
2849
var_a: equ var_z + 4 ;4 bytes
2850
var_b: equ var_a + 4 ;4 bytes
2851
var_c: equ var_b + 4 ;4 bytes
2852
temp: equ var_c + 4 ;4 bytes
2853
temp1: equ temp + 4 ;4 bytes
2854
temp2: equ temp1 + 4 ;4 bytes
2855
temp3: equ temp2 + 4 ;4 bytes
2857
pow10exp_single: equ scrap+9
2858
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
2860
;---------------------------------------------------------------------------------------------------------
2862
;---------------------------------------------------------------------------------------------------------
2864
;;Still need to tend to special cases
2932
pop hl ;bigger float
3064
;;Need to adjust sign flag
3087
;;How many push/pops are needed?
3095
;;How many push/pops are needed?
3101
;;How many push/pops are needed?
3102
;;Return bigger number
3109
;---------------------------------------------------------------------------------------------------------
3111
;---------------------------------------------------------------------------------------------------------
3134
jp addInject ;jumps in to the addSingle routine
3136
;---------------------------------------------------------------------------------------------------------
3138
;---------------------------------------------------------------------------------------------------------
3141
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
3142
;Outputs: float1*float2 is stored to (BC)
3143
;573+mul24+{0,35}+{0,30}
3146
;avg: 2055.13839751681cc
3172
;;return float in CHLB
3182
jr z,mulSingle_case0
3194
;jr z,mulSingle_case1
3198
jp z,mulSingle_case1
3203
rra ; |Lots of help from Runer112 and
3204
adc a,a ; |calc84maniac for optimizing
3205
jp po,bad ; |this exponent check.
3214
call mul24 ;BDE*CHL->HLBCDE, returns sign info
3271
;special*x = special
3292
;basically, if b|c has bit 5 set, return NaN
3325
;;avg :1464.9033203125cc (1464+925/1024)
3328
;avg: 1449.63839751681cc
3369
;---------------------------------------------------------------------------------------------------------
3371
;---------------------------------------------------------------------------------------------------------
3374
;;HL points to numerator
3375
;;DE points to denominator
3376
;;BC points to where the quotient gets written
3378
divSingle_no_pushpop:
3384
xor (hl) ; |Get sign of output
3391
ex de,hl ; |Get exponent
3498
call divsub1 ;34 or 66
3516
;34cc or 66cc or 93cc
3531
;---------------------------------------------------------------------------------------------------------
3533
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
3534
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
3535
;---------------------------------------------------------------------------------------------------------
3536
;double mypow( double base, double power, double precision )
3538
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3539
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3540
; else if ( precision >= 1 ) {
3541
; if( base >= 0 ) return sqrt( base );
3542
; else return sqrt( -base );
3543
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3546
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
3552
;;BC points to output
3556
ld bc, var_y ; power
3561
ld hl, const_precision
3562
ld bc, var_a ; precision
3565
ld bc, var_z ; result
3574
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3580
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3586
; else if ( precision >= 1 ) {
3587
; if( base >= 0 ) return sqrt( base );
3588
; else return sqrt( -base );
3594
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3619
; return 1 / mypow( base, -power, precision );
3638
; return base * mypow( base, power-1, precision );
3657
; if( base >= 0 ) return sqrt( base );
3658
; else return sqrt( -base );
3684
; 2^x = 1.000000001752 + x * (0.693146989552 + x * (0.2402298085906 + x * (5.54833215071e-2 + x * (9.67907584392e-3 + x * (1.243632065103e-3 + x * 2.171671843714e-4)))))
3685
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
3686
;In this case, our polynomial is the same degree, with error different by less than 1 bit, so it's just a waste to range-reduce in this way.
3689
;x-=int(x) ;leaves x in [0,1)
3691
;;if x==inf -> out==inf
3692
;;if x==-inf -> out==0
3693
;;if x==NAN -> out==NAN
3700
push af ;keep track of sign
3710
jr c,_pow_1 ;int(x)=0
3723
jr nz,exp_normalized
3734
jr exp_normalized ;.db $11 ;start of `ld de,**`
3741
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
3750
jp z,exp_underflow+1
3751
;perform 1-(var48+10)--> var48+10
3759
;our 'x' is at var48+10
3760
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
3761
;uses 14 bytes of RAM
3803
;-inf -> +0 because lim approaches 0 from the right
3825
;-inf -> +0 because lim approaches 0 from the right
3827
sbc a,a ;FF if should be 0,
3842
;---------------------------------------------------------------------------------------------------------
3844
;---------------------------------------------------------------------------------------------------------
3846
if defined MATH_SQR or defined MATH_EXP
3848
;Uses 3 bytes at scrap
3850
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
3869
jp z,sqrtSingle_special
3872
push af ;new exponent
3882
;AHL is the new remainder
3883
;Need to divide by 2, then divide by the 16-bit (var_x+4)
3887
;We are just going to approximate it
3969
;Output: DE is the sqrt, AHL is the remainder
3970
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
3994
jr _15a ;.db $FE ;start of `cp *`
4008
jr _16a ;.db $FE ;start of `cp *`
4022
jr _17a ;.db $FE ;start of `cp *`
4036
jr _18a ;.db $FE ;start of `cp *`
4040
;Now we have four more iterations
4041
;The first two are no problem
4053
jr _19a ;.db $FE ;start of `cp *`
4067
jr _20a ;.db $FE ;start of `cp *`
4072
;On the next iteration, HL might temporarily overflow by 1 bit
4074
rl d ;sla e \ rl d \ inc e
4078
adc hl,hl ;This might overflow!
4079
jr c,sqrt32_iter15_br0
4092
;On the next iteration, HL is allowed to overflow, DE could overflow with our current routine, but it needs to be shifted right at the end, anyways
4095
ld b,a ;either 0x00 or 0x80
4116
;returns A as the sqrt, HL as the remainder, D = 0
4130
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4141
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4152
dec d ;this resets the low bit of D, so `srl d` resets carry.
4153
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
4175
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4188
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4210
;---------------------------------------------------------------------------------------------------------
4212
;---------------------------------------------------------------------------------------------------------
4214
if defined MATH_LOG or defined MATH_LN
4217
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
4218
; a * x ^ (1/a) - a, where a = 100
4221
ld de, const_100_inv
4223
call powSingle ; temp = x ^ (1/100)
4227
call mulSingle ; temp1 = temp * 100
4230
call subSingle ; bc = temp1 - 100
4235
;---------------------------------------------------------------------------------------------------------
4237
;---------------------------------------------------------------------------------------------------------
4254
;---------------------------------------------------------------------------------------------------------
4256
;---------------------------------------------------------------------------------------------------------
4263
;;BC points to the output
4268
;;DE points to lg(y), HL points to x, BC points to output
4277
;---------------------------------------------------------------------------------------------------------
4279
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
4280
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
4281
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
4282
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
4283
;---------------------------------------------------------------------------------------------------------
4285
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4288
; taylor: x - x^3/6 + x^5/120 - x^7/5040
4289
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
4291
; var_b = round( x / (2*PI), 0 )
4292
; var_c = x - var_b*2*PI
4293
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4294
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4295
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4302
call copySingle ; return 0
4306
call trigRangeReductionSinCos
4311
call mulSingle ; var_b = var_a * var_a
4315
call mulSingle ; temp = x^2/5040
4319
call subSingle ; temp1 = 1/120 - temp
4323
call mulSingle ; temp = x^2 * temp1
4327
call subSingle ; temp1 = 1/6 - temp
4331
call mulSingle ; temp = x^2 * temp1
4335
call subSingle ; temp1 = 1 - temp
4339
call mulSingle ; return x * temp1
4342
trigRangeReductionSinCos:
4345
; var_b = round( x / (2*PI), 0 )
4353
; var_c = x - var_b*2*PI
4357
call mulSingle ; temp = var_b*2*PI
4361
call subSingle ; var_c = x - temp
4362
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4366
jr nc, trigRangeReductionSinCos.else.2
4369
call copySingle ; temp1 = var_c
4370
jr trigRangeReductionSinCos.endif.2
4371
trigRangeReductionSinCos.else.2:
4375
call addSingle ; temp1 = var_c + 2*PI
4376
trigRangeReductionSinCos.endif.2:
4377
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4381
jr c, trigRangeReductionSinCos.else.3
4382
jr z, trigRangeReductionSinCos.else.3
4386
call subSingle ; temp2
4387
jr trigRangeReductionSinCos.endif.3
4388
trigRangeReductionSinCos.else.3:
4391
call copySingle ; temp2 = temp1
4392
trigRangeReductionSinCos.endif.3:
4393
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4394
ld hl, const_half_pi
4397
jr c, trigRangeReductionSinCos.else.4
4398
jr z, trigRangeReductionSinCos.else.4
4402
call subSingle ; var_a
4403
jr trigRangeReductionSinCos.endif.4
4404
trigRangeReductionSinCos.else.4:
4407
call copySingle ; var_a = temp2
4408
trigRangeReductionSinCos.endif.4:
4409
; if( temp > PI, -1, 1 )
4413
jr nc, trigRangeReductionSinCos.endif.5
4417
ld (ix+2), a ; turn var_a to negative
4418
trigRangeReductionSinCos.endif.5:
4424
;---------------------------------------------------------------------------------------------------------
4426
;---------------------------------------------------------------------------------------------------------
4428
if defined MATH_COS or defined MATH_TAN
4431
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
4432
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4433
; reduction: same as sin
4442
call copySingle ; return 1
4446
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4447
call trigRangeReductionSinCos
4452
call mulSingle ; var_b = var_a * var_a
4456
call mulSingle ; temp = x^2/720
4460
call subSingle ; temp1 = 1/24 - temp
4464
call mulSingle ; temp = x^2 * temp1
4468
call subSingle ; temp1 = 1/2 - temp
4472
call mulSingle ; temp = x^2 * temp1
4476
call subSingle ; temp1 = 1 - temp
4478
; temp3 = abs(var_c)
4479
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
4486
ld (ix+2), a ; temp3 = abs(var_c)
4488
ld de, const_half_pi
4489
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
4490
jr nc, cosSingle.endif.1
4494
ld (ix+2), a ; temp1 = -temp1
4498
call copySingle ; return temp1
4503
;---------------------------------------------------------------------------------------------------------
4505
;---------------------------------------------------------------------------------------------------------
4526
;---------------------------------------------------------------------------------------------------------
4528
;---------------------------------------------------------------------------------------------------------
4533
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4534
; x < -1: atan - PI/2
4535
; x >= 1: PI/2 - atan
4536
;reduction: abs(X) > 1 : Y = 1 / X
4537
; abs(X) <= 1: Y = X
4546
call copySingle ; return 0
4550
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4551
call trigRangeReductionAtan
4557
call mulSingle ; var_b = var_a * var_a
4561
call mulSingle ; temp = (4*x)^2
4565
call divSingle ; temp1 = temp/9
4569
call addSingle ; temp = 7 + temp1
4573
call mulSingle ; temp1 = var_b * 9
4577
call divSingle ; temp2 = temp1 / temp
4581
call addSingle ; temp = 5 + temp2
4585
call mulSingle ; temp1 = var_b * 4
4589
call divSingle ; temp2 = temp1 / temp
4593
call addSingle ; temp = 3 + temp2
4597
call divSingle ; temp2 = var_b / temp
4601
call addSingle ; temp = 1 + temp2
4605
call divSingle ; temp2 = var_a / temp
4607
; x >= 1: PI/2 - atan
4611
ld hl, const_half_pi
4618
; x < -1: atan - PI/2
4629
ld de, const_half_pi
4638
call copySingle ; return temp2
4641
trigRangeReductionAtan:
4642
;reduction: abs(X) > 1 : Y = 1 / X
4643
; abs(X) <= 1: Y = X
4652
ld (ix+2), a ; abs(x)
4656
jr nc, trigRangeReductionAtan.1
4662
jr trigRangeReductionAtan.2
4663
trigRangeReductionAtan.1:
4668
trigRangeReductionAtan.2:
4672
jr c, trigRangeReductionAtan.3
4676
ld (ix+2), a ; y = -y
4677
trigRangeReductionAtan.3:
4682
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4684
;---------------------------------------------------------------------------------------------------------
4686
;---------------------------------------------------------------------------------------------------------
4700
;---------------------------------------------------------------------------------------------------------
4702
;---------------------------------------------------------------------------------------------------------
4771
if defined MATH_ABSFN
4773
;---------------------------------------------------------------------------------------------------------
4775
;---------------------------------------------------------------------------------------------------------
4778
;;HL points to the float
4779
;;BC points to where to output the result
4798
;---------------------------------------------------------------------------------------------------------
4800
;---------------------------------------------------------------------------------------------------------
4803
;;HL points to the float
4804
;;BC points to where to output the result
4809
if defined powSingle or defined sgnSingle or defined MATH_NEG
4811
;---------------------------------------------------------------------------------------------------------
4813
;---------------------------------------------------------------------------------------------------------
4816
;;HL points to the float
4817
;;BC points to where to output the result
4823
jr nz, negSingle.test.sign
4826
jr nz, negSingle.test.sign
4829
jr nz, negSingle.test.sign
4832
jr nz, negSingle.test.sign
4843
negSingle.test.sign:
4846
jr z, negSingle.positive
4850
call negSingle.positive
4869
if defined MATH_DCOMP or defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN or defined MATH_SIN or defined MATH_TAN or defined MATH_COS or defined MATH_ATN
4871
;---------------------------------------------------------------------------------------------------------
4873
;---------------------------------------------------------------------------------------------------------
4876
;Input: HL points to float1, DE points to float2
4878
; float1 >= float2 : nc
4879
; float1 < float2 : c,nz
4880
; float1 == float2 : z
4881
; There is a margin of error allowed in the lower 2 bits of the mantissa.
4883
;Currently fails when both numbers have magnitude less than about 2^-106
4918
ld a,(scrap+3) ;new power
4919
pop bc ;B is old power
4929
or 1 ;not equal, so reset z flag
4930
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
4940
;---------------------------------------------------------------------------------------------------------
4942
;---------------------------------------------------------------------------------------------------------
4945
;Stores a pseudo-random number on [0,1)
4946
;it won't produce values on (0,2^-23)
4955
;DEHL is the mantissa, B is the exponent
4971
;If we needed to shift more than 8 bits, we'll load in more random data
4976
jp nc,rand_no_more_rand_data
4984
rand_no_more_rand_data:
5003
;;Tested and passes all CAcert tests
5004
;;Uses a very simple 32-bit LCG and 32-bit LFSR
5005
;;it has a period of 18,446,744,069,414,584,320
5006
;;roughly 18.4 quintillion.
5007
;;LFSR taps: 0,2,6,7 = 11000101
5009
;;Thanks to Runer112 for his help on optimizing the LCG and suggesting to try the much simpler LCG. On their own, the two are terrible, but together they are great.
5010
;Uses 64 bits of state
5046
if defined MATH_FOUT
5048
;---------------------------------------------------------------------------------------------------------
5050
; in HL = Single address
5051
; BC = String address
5052
; out A = String size
5053
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
5054
; http://0x80.pl/articles/convert-float-to-integer.html
5055
;---------------------------------------------------------------------------------------------------------
5069
; Move the float to scrap
5073
; Make the float negative, write a '-' if already negative
5082
ld a,'-' ; write '-' simbol
5090
; Check if the exponent field is 0 (a special value)
5097
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
5105
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
5113
ld (pow10exp_single),a ;The base-10 exponent
5117
ld de,pow10LUT ;get the table of 10^-(2^k)
5119
ld hl, pow10exp_single
5121
call singletostr_mul
5122
call singletostr_mul
5123
call singletostr_mul
5124
call singletostr_mul
5125
call singletostr_mul
5126
call singletostr_mul
5127
;now the number is pretty close to a nice value
5129
; If it is less than 1, multiply by 10
5134
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
5140
ld hl,pow10exp_single
5146
; Convert to a fixed-point number !
5160
;We need to get 7 digits
5162
pop hl ;Points to the string
5164
;The first digit can be as large as 20, so it'll actually be two digits
5168
;Increment the exponent :)
5169
ld de,(pow10exp_single-1)
5171
ld (pow10exp_single-1),de
5180
; Get the remaining digits.
5187
call singletostrmul10
5192
;Save the pointer to the end of the string
5199
jr c,rounding_done_single
5200
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
5209
rounding_done_single:
5212
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
5224
;Now lets move HL-DE bytes at DE+1 to DE
5236
;If z flag is reset, this means that the exponent should be bumped up 1
5237
ld a,(pow10exp_single)
5240
ld (pow10exp_single),a
5243
;if -4<=A<=6, then need to insert the decimal place somewhere.
5248
;for this, we need to insert the decimal after the first digit
5249
;Then, we need to append the exponent string
5251
ld de,strout_single-1
5253
cp '-' ;negative sign
5261
;remove any stray zeroes at the end before appending the exponent
5265
; Write the exponent
5268
ld a,(pow10exp_single)
5271
ld (hl),'-' ;negative sign
5289
ld de, strout_single
5292
ld a, l ; string size
5294
ld hl,strout_single-1
5298
ld a,(pow10exp_single)
5302
;need to put zeroes before everything
5305
cp '-' ;negative sign
5331
ld de,strout_single-1
5335
cp '-' ;negative sign
5346
ld hl,strout_single-1
5364
;multiply the 0.24 fixed point number at scrap by 10
5365
;overflow in A register
5400
;Check that the last digit isn't a decimal!
5454
;---------------------------------------------------------------------------------------------------------
5456
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
5457
;---------------------------------------------------------------------------------------------------------
5462
ptr_sto: equ scrap+9
5464
;;#Routines/Single Precision
5466
;; HL points to the string
5467
;; BC points to where the float is output
5469
;; scrap+9 is the pointer to the end of the string
5471
;; 11 bytes at scrap ?
5476
;Check if there is a negative sign.
5485
;Skip all leading zeroes
5488
jr z,$-4 ;jumps back to the `inc hl`
5491
;Check if the next char is char_DEC
5493
or a ;to reset the carry flag
5495
jr _54a ;.db $FE ;start of cp *
5502
jr z,$-5 ;jumps back to the `dec b`
5505
;Now we read in the next 8 digits
5511
;Now `scrap` holds the 4-digit base-100 number.
5513
;if carry flag is set, just need to get rid of remaining digits
5514
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
5525
jp z,strToSingle_inf
5528
;Now check for engineering `E` to modify the exponent
5532
;Gotta multiply the number at (scrap) by 2^24
5535
call scrap_times_256
5538
call scrap_times_256
5541
call scrap_times_256
5544
call scrap_times_256
5547
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
5555
jp z,strToSingle_zero-1
5559
jp m,strToSingle_normed
5560
;Will need to iterate at most three times
5573
;Move the number to scrap
5582
;now (scrap) is our number, need to multiply by power of 10!
5583
;Power of 10 is stored in B, need to put in A first
5591
jp nc,strToSingle_inf+1
5594
jp nc,strToSingle_zero
5618
cp char_NEG ;negative exponent?
5670
call scrap_times_sub
5683
jr nz,strToSingle_inf
5701
if defined roundSingle or defined MATH_FRCSGL
5703
;---------------------------------------------------------------------------------------------------------
5705
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
5706
;---------------------------------------------------------------------------------------------------------
5713
ld l, (ix) ; convert integer parameter to single float
5715
ld bc, 0x1000 ; bynary digits count + sign
5717
int2Single.test.zero:
5719
or h ; test if hl is not zero
5720
jr nz, int2Single.test.negative
5722
jr nz, int2Single.test.negative
5727
int2Single.test.negative:
5728
bit 7, h ; test if hl is negative
5729
jr z, int2Single.normalize
5730
ld c, 0x80 ; sign negative
5739
int2Single.normalize:
5742
jr nz, int2Single.mount
5745
jr int2Single.normalize
5748
res 7, h ; turn off upper bit
5750
ld a, c ; restore sign
5752
ld h, a ; ...into upper mantissa
5754
ld e, h ; sign+mantissa
5755
ld h, l ; high mantissa
5756
ld l, 0 ; low mantissa
5758
ld a, b ; binary digits count
5759
or 0x80 ; exponent bias
5764
ld (ix), l ; low mantissa
5765
ld (ix+1), h ; high mantissa
5766
ld (ix+2), e ; sign + mantissa
5767
ld (ix+3), d ; expoent
5776
if defined roundSingle or defined MATH_FRCINT
5778
;---------------------------------------------------------------------------------------------------------
5780
; http://0x80.pl/articles/convert-float-to-integer.html
5781
;---------------------------------------------------------------------------------------------------------
5784
; HL points to the single-precision float
5786
; HL is the 16-bit signed integer part of the float
5787
; BC points to 16-bit signed integer
5804
jr c,no_shift_single_to_int16
5806
jr nc,no_shift_single_to_int16
5828
jr _67a ;.db $11 ;start of ld de,*
5840
no_shift_single_to_int16:
5862
;---------------------------------------------------------------------------------------------------------
5863
; Auxiliary routines
5864
;---------------------------------------------------------------------------------------------------------
5871
const_pi: db $DB,$0F,$49,$81
5872
const_e: db $54,$f8,$2d,$81
5873
const_lg_e: db $3b,$AA,$38,$80
5874
const_ln_2: db $18,$72,$31,$7f
5875
const_log2: db $9b,$20,$1a,$7e
5876
const_lg10: db $78,$9a,$54,$81
5877
const_0: db $00,$00,$00,$00
5878
const_1: db $00,$00,$00,$80
5879
const_2: dw 0, 33024
5880
const_3: dw 0, 33088
5881
const_4: dw 0, 33280
5882
const_5: dw 0, 33312
5883
const_7: dw 0, 33376
5884
const_9: dw 0, 33552
5885
const_16: dw 0, 33792
5886
const_100: db $00,$00,$48,$86
5887
const_100_inv: dw 55050, 31011
5888
const_precision: db $77,$CC,$2B,$65 ;10^-8
5889
const_half_1: dw 0, 32512
5890
const_inf: db $00,$00,$40,$00
5891
const_NegInf: db $00,$00,$C0,$00
5892
const_NaN: db $00,$00,$20,$00
5893
const_log10_e: db $D9,$5B,$5E,$7E
5894
const_2pi: db $DB,$0F,$49,$82
5895
const_2pi_inv: db $83,$F9,$22,$7D
5896
const_half_pi: dw 4059, 32841
5897
const_p25: db $00,$00,$00,$7E
5898
const_p5: db $00,$00,$00,$7F
5901
sin_a1: dw 43691, 32042
5902
sin_a2: dw 34952, 30984
5903
sin_a3: dw 3329, 29520
5904
cos_a1: equ const_half_1
5905
cos_a2: dw 43691, 31530
5906
cos_a3: dw 2914, 30262
5907
exp_a1: db $15,$72,$31,$7F ;.693146989552
5908
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
5909
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
5910
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
5911
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
5912
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
5913
const_1p40625: db $00,$00,$34,$80 ;1.40625
5915
if defined MATH_CONSTSINGLE
5923
;A is the constant ID#
5924
;returns nc if failed, c otherwise
5925
;HL points to the constant
5926
cp (end_const-start_const)>>2
5933
;#if ((end_const-4)>>8)!=(start_const>>8)
5946
db $CD,$CC,$4C,$7C ;.1
5947
db $0A,$D7,$23,$79 ;.01
5948
db $17,$B7,$51,$72 ;.0001
5949
db $77,$CC,$2B,$65 ;10^-8
5950
db $95,$95,$66,$4A ;10^-16
5951
db $1F,$B1,$4F,$15 ;10^-32
5954
db $00,$00,$20,$83 ;10
5955
db $00,$00,$48,$86 ;100
5956
db $00,$40,$1C,$8D ;10000
5957
db $20,$BC,$3E,$9A ;10^8
5958
db $CA,$1B,$0E,$B5 ;10^16
5959
db $AE,$C5,$1D,$EA ;10^32
5966
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
5967
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
5968
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
5969
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
5970
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
5971
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
5972
;C>=2 15+{0,20+{0,8}}
5975
;avg: 349.21279907227cc
6066
;26 bytes, adds 118cc to the traditional routine
6101
;c flag means don't increment the exponent
6104
jr c,ascii_to_uint8_noexp
6106
jr z,ascii_to_uint8_noexp-2
6110
jr nc,ascii_to_uint8_noexp_end
6122
jr z,ascii_to_uint8_noexp_2nd
6126
jr nc,ascii_to_uint8_noexp_end
6137
ascii_to_uint8_noexp:
6140
jr nc,ascii_to_uint8_noexp_end
6147
ascii_to_uint8_noexp_2nd:
6152
jr nc,ascii_to_uint8_noexp_end
6155
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
6156
ascii_to_uint8_noexp_end:
6166
if defined MATH_RSUBSINGLE
6187
jp addInject ;jumps in to the addSingle routine
6191
if defined MATH_MOD1SINGLE
6193
;This routine performs `x mod 1`, returning a non-negative value.
6216
jr z,mod1Single_special
6229
;If it is zero, need to set exponent to zero and return
6252
;make sure it isn't zero else we need to add 1
6264
;If INF, need to return NaN instead
6265
;For 0 and NaN, just return itself :)
6285
if defined MATH_FOUT
6287
; --------------------------------------------------------------
6288
; Converts a signed integer value to a zero-terminated ASCII
6289
; string representative of that value (using radix 10).
6291
; Brandon Wilson WikiTI
6292
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
6293
; --------------------------------------------------------------
6295
; HL Value to convert (two's complement integer).
6296
; DE Base address of string destination. (pointer).
6297
; --------------------------------------------------------------
6300
; --------------------------------------------------------------
6301
; REGISTERS/MEMORY DESTROYED
6303
; --------------------------------------------------------------
6309
; Detect sign of HL.
6313
; HL is negative. Output '-' to string and negate HL.
6318
; Negate HL (using two's complement)
6322
ld a, 0 ; Note that XOR A or SUB A would disturb CF
6326
; Convert HL to digit characters
6328
ld b, 0 ; B will count character length of number
6331
call div_hl_c; HL = HL / A, A = remainder
6338
; Retrieve digits from stack
6346
; Terminate string with NULL
6357
ld a, l ; string size
6365
;===============================================================
6366
; Convert a string of base-10 digits to a 16-bit value.
6367
; http://z80-heaven.wikidot.com/math#toc32
6369
; DE points to the base 10 number string in RAM.
6371
; HL is the 16-bit value of the number
6372
; DE points to the byte after the number
6377
; A (actually, add 30h and you get the ending token)
6380
; n is the number of digits
6382
; at most 595 cycles for any 16-bit decimal value
6383
;===============================================================
6386
ld hl,0 ; 10 : 210000
6403
jr nc,ConvLoop ;12|23: 30EE
6405
jr ConvLoop ; --- : 18EB
6412
; return remainder in a
6413
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
6434
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
6464
djnz div_dehl_c.loop
6472
;---------------------------------------------------------------------------------------------------------
6473
; VARIABLES INITIALIZE
6474
;---------------------------------------------------------------------------------------------------------
6478
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
6479
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
6480
ld (VAR_DUMMY.POINTER), hl
6481
ld b, VAR_DUMMY.LENGTH
6486
djnz INITIALIZE_DUMMY.1
6491
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
6493
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
6496
INITIALIZE_VARIABLES:
6497
call INITIALIZE_DATA
6498
call INITIALIZE_DUMMY
6501
call gfxInitSpriteCollisionTable
6504
;if defined COMPILE_TO_ROM
6505
; ld ix, BIOS_JIFFY ; initialize rom clock
6513
ld d, 2 ; any = default integer
6514
ld c, 0 ; variable name 1 (variable number)
6515
ld b, 0 ; variable name 2 (type flag=any)
6516
call INIT_VAR ; variable initialize
6518
ld d, 2 ; any = default integer
6519
ld c, 1 ; variable name 1 (variable number)
6520
ld b, 0 ; variable name 2 (type flag=any)
6521
call INIT_VAR ; variable initialize
6523
ld d, 2 ; any = default integer
6524
ld c, 2 ; variable name 1 (variable number)
6525
ld b, 0 ; variable name 2 (type flag=any)
6526
call INIT_VAR ; variable initialize
6528
ld d, 2 ; any = default integer
6529
ld c, 3 ; variable name 1 (variable number)
6530
ld b, 0 ; variable name 2 (type flag=any)
6531
call INIT_VAR ; variable initialize
6533
ld d, 2 ; any = default integer
6534
ld c, 4 ; variable name 1 (variable number)
6535
ld b, 0 ; variable name 2 (type flag=any)
6536
call INIT_VAR ; variable initialize
6538
ld d, 2 ; any = default integer
6539
ld c, 5 ; variable name 1 (variable number)
6540
ld b, 0 ; variable name 2 (type flag=any)
6541
call INIT_VAR ; variable initialize
6545
;---------------------------------------------------------------------------------------------------------
6546
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
6547
;---------------------------------------------------------------------------------------------------------
6549
if defined COMPILE_TO_ROM
6552
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
6554
if pgmPage1.pad >= 0
6557
; .WARNING "There's no free space left on program page 1"
6562
VAR_STACK.START: equ ramArea
6563
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
6565
VAR_STACK.POINTER: equ VAR_STACK.START
6567
PRINT.CRLF: db 3, 0, 0, 2
6568
dw PRINT.CRLF.DATA, 0, 0, 0
6569
PRINT.CRLF.DATA: db 13,10,0
6571
PRINT.TAB: db 3, 0, 0, 1
6572
dw PRINT.TAB.DATA, 0, 0, 0
6573
PRINT.TAB.DATA: db 09,0
6576
LIT_NULL_DBL: dw 0, 0, 0, 0
6582
LIT_QUOTE_CHAR: db '\"'
6585
LIT_TRUE: db 2, 0, 0
6589
LIT_FALSE: db 2, 0, 0
6594
LIT_5: db 3, 0, 0, 35
6597
LIT_5_DATA: db "<<< Expression test - variables >>>", 0
6600
IDF_6: equ VAR_STACK.POINTER + 0
6607
LIT_8: db 3, 0, 0, 4
6610
LIT_8_DATA: db "1 = ", 0
6617
LIT_12: db 3, 0, 0, 8
6618
dw LIT_12_DATA, 0, 0
6620
LIT_12_DATA: db "1 + 2 = ", 0
6623
IDF_13: equ VAR_STACK.POINTER + 11
6638
LIT_17: db 3, 0, 0, 12
6639
dw LIT_17_DATA, 0, 0
6641
LIT_17_DATA: db "1 + 2 + 3 = ", 0
6660
LIT_23: db 3, 0, 0, 16
6661
dw LIT_23_DATA, 0, 0
6663
LIT_23_DATA: db "1 + 2 + 3 * 4 = ", 0
6690
LIT_31: db 3, 0, 0, 26
6691
dw LIT_31_DATA, 0, 0
6693
LIT_31_DATA: db "1 + 2 + 3 * 4 + 50 - 10 = ", 0
6696
IDF_32: equ VAR_STACK.POINTER + 22
6703
LIT_35: db 3, 0, 0, 7
6704
dw LIT_35_DATA, 0, 0
6706
LIT_35_DATA: db "-100 = ", 0
6709
IDF_36: equ VAR_STACK.POINTER + 33
6716
LIT_38: db 3, 0, 0, 13
6717
dw LIT_38_DATA, 0, 0
6719
LIT_38_DATA: db "-100 + 200 = ", 0
6722
IDF_39: equ VAR_STACK.POINTER + 44
6729
IDF_41: equ VAR_STACK.POINTER + 55
6744
LIT_46: db 3, 0, 0, 21
6745
dw LIT_46_DATA, 0, 0
6747
LIT_46_DATA: db "2 * ( 10 + 3 ) / 4 = ", 0
6749
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 66
6751
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
6752
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
6753
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
6754
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
6756
VAR_DUMMY.SIZE: equ 8
6757
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
6758
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
6759
VAR_STACK.END: equ VAR_DUMMY.END + 1
6761
;--------------------------------------------------------
6763
;--------------------------------------------------------
6766
DATA_ITEMS_COUNT: equ 0
6768
DATA_SET_ITEMS_START:
6769
DATA_SET_ITEMS_COUNT: equ 0
6772
;---------------------------------------------------------------------------------------------------------
6774
;---------------------------------------------------------------------------------------------------------
6776
if defined COMPILE_TO_ROM
6780
pgmPage2.pad: equ romSize - (romPad - pgmArea)
6782
if pgmPage2.pad >= 0
6785
if pgmPage2.pad < lowLimitSize
6786
.WARNING "There's only less than 5% free space on this ROM"
6789
.ERROR "There's no free space left on this ROM"
6794
end_file: end start_pgm ; label start is the entry point